Skip to content

Commit 420cff9

Browse files
Lagrang3rustyrussell
authored andcommitted
askrene: mcf: trade granularity for performance
Speed in getroutes up by setting the granularity to 1000 Amount (sats) | speedup ----------------------- 100 | 1.00 1000 | 1.00 10000 | 1.06 100000 | 1.31 1000000 | 2.64 Worst runtime of getroutes Amount (sats) | before (ms) | after (ms) -------------------------------------- 100 | 1507 | 761 1000 | 2129 | 1214 10000 | 1632 | 1043 100000 | 2004 | 1150 1000000 | 27170 | 3289 Changelog-None Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
1 parent b7f3d7b commit 420cff9

File tree

3 files changed

+24
-18
lines changed

3 files changed

+24
-18
lines changed

plugins/askrene/mcf.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -969,10 +969,15 @@ struct flow **minflow(const tal_t *ctx,
969969
params->source = source;
970970
params->target = target;
971971
params->amount = amount;
972-
params->accuracy = AMOUNT_MSAT(1000);
973-
/* FIXME: params->accuracy = amount_msat_max(amount_msat_div(amount,
974-
* 1000), AMOUNT_MSAT(1));
972+
/* -> We reduce the granularity of the flow by limiting the subdivision
973+
* of the payment amount into 1000 units of flow. That reduces the
974+
* computational burden for algorithms that depend on it, eg. "capacity
975+
* scaling" and "successive shortest path".
976+
* -> Using Ceil operation instead of Floor so that
977+
* accuracy x 1000 >= amount
975978
* */
979+
params->accuracy =
980+
amount_msat_max(AMOUNT_MSAT(1), amount_msat_div_ceil(amount, 1000));
976981

977982
// template the channel partition into linear arcs
978983
params->cap_fraction[0]=0;

tests/test_askrene.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1204,10 +1204,10 @@ def test_real_data(node_factory, bitcoind):
12041204
# CI, it's slow.
12051205
if SLOW_MACHINE:
12061206
limit = 25
1207-
expected = (5, 25, 1567535, 142772, 91)
1207+
expected = (6, 25, 1568821, 143649, 91)
12081208
else:
12091209
limit = 100
1210-
expected = (9, 96, 6563767, 629671, 91)
1210+
expected = (9, 96, 6565467, 630668, 91)
12111211

12121212
fees = {}
12131213
for n in range(0, limit):
@@ -1324,7 +1324,7 @@ def test_real_biases(node_factory, bitcoind):
13241324
expected = ({1: 6, 2: 6, 4: 7, 8: 12, 16: 14, 32: 19, 64: 25, 100: 25}, 0)
13251325
else:
13261326
limit = 100
1327-
expected = ({1: 22, 2: 25, 4: 36, 8: 52, 16: 69, 32: 80, 64: 96, 100: 96}, 0)
1327+
expected = ({1: 22, 2: 25, 4: 36, 8: 53, 16: 69, 32: 80, 64: 96, 100: 96}, 0)
13281328

13291329
l1.rpc.askrene_create_layer('biases')
13301330
num_changed = {}

tests/test_xpay.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ def test_xpay_bolt12_no_mpp(node_factory, chainparams, deprecations):
671671

672672
# Amount needs to be enought that it bothers splitting, but not
673673
# so much that it can't pay without mpp.
674-
AMOUNT = 500000000
674+
AMOUNT = 800000000
675675

676676
# l2 will advertize mpp, l3 won't.
677677
l2offer = l2.rpc.offer(AMOUNT, 'test_xpay_bolt12_no_mpp')
@@ -686,10 +686,11 @@ def test_xpay_bolt12_no_mpp(node_factory, chainparams, deprecations):
686686
assert ret['failed_parts'] == 0
687687
if deprecations:
688688
assert ret['successful_parts'] == 2
689+
assert ret['amount_sent_msat'] == AMOUNT + AMOUNT // 100000 + 2
689690
else:
690691
assert ret['successful_parts'] == 1
692+
assert ret['amount_sent_msat'] == AMOUNT + AMOUNT // 100000 + 1
691693
assert ret['amount_msat'] == AMOUNT
692-
assert ret['amount_sent_msat'] == AMOUNT + AMOUNT // 100000 + 1
693694

694695

695696
def test_xpay_slow_mode(node_factory, bitcoind):
@@ -706,18 +707,18 @@ def test_xpay_slow_mode(node_factory, bitcoind):
706707
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 10)
707708

708709
# First try an MPP which fails
709-
inv = l5.rpc.invoice(500000000, 'test_xpay_slow_mode_fail', 'test_xpay_slow_mode_fail', preimage='01' * 32)['bolt11']
710+
inv = l5.rpc.invoice(800000000, 'test_xpay_slow_mode_fail', 'test_xpay_slow_mode_fail', preimage='01' * 32)['bolt11']
710711
l5.rpc.delinvoice('test_xpay_slow_mode_fail', status='unpaid')
711712

712713
with pytest.raises(RpcError, match=r"Destination said it doesn't know invoice: incorrect_or_unknown_payment_details"):
713714
l1.rpc.xpay(inv)
714715

715716
# Now a successful one
716-
inv = l5.rpc.invoice(500000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
717+
inv = l5.rpc.invoice(800000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
717718

718719
assert l1.rpc.xpay(inv) == {'payment_preimage': '00' * 32,
719-
'amount_msat': 500000000,
720-
'amount_sent_msat': 500010002,
720+
'amount_msat': 800000000,
721+
'amount_sent_msat': 800016004,
721722
'failed_parts': 0,
722723
'successful_parts': 2}
723724

@@ -739,7 +740,7 @@ def test_fail_after_success(node_factory, bitcoind, executor, slow_mode):
739740
bitcoind.generate_block(5)
740741
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 10)
741742

742-
inv = l5.rpc.invoice(500000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
743+
inv = l5.rpc.invoice(800000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
743744
fut = executor.submit(l1.rpc.xpay, invstring=inv, retry_for=0)
744745

745746
# Part via l3 is fine. Part via l4 is stuck, so we kill l4 and mine
@@ -750,8 +751,8 @@ def test_fail_after_success(node_factory, bitcoind, executor, slow_mode):
750751
# Normally, we return as soon as first part succeeds.
751752
if slow_mode is False:
752753
assert fut.result(TIMEOUT) == {'payment_preimage': '00' * 32,
753-
'amount_msat': 500000000,
754-
'amount_sent_msat': 500010002,
754+
'amount_msat': 800000000,
755+
'amount_sent_msat': 800016004,
755756
'failed_parts': 0,
756757
'successful_parts': 2}
757758

@@ -763,15 +764,15 @@ def test_fail_after_success(node_factory, bitcoind, executor, slow_mode):
763764
l1.daemon.wait_for_log(r"UNUSUAL.*Destination accepted partial payment, failed a part \(Error permanent_channel_failure for path ->022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59->0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199->032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e, from 022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59\)")
764765
# Could be either way around, check both
765766
line = l1.daemon.is_in_log(r"UNUSUAL.*Destination accepted partial payment, failed a part")
766-
assert re.search(r'but accepted only 32000msat of 500000000msat\. Winning\?!', line) or re.search(r'but accepted only 499968000msat of 500000000msat\. Winning\?!', line)
767+
assert re.search(r'but accepted only .* of 800000000msat\. Winning\?!', line)
767768

768769
if slow_mode is True:
769770
# Now it succeeds, but notes that it only sent one part!
770771
res = fut.result(TIMEOUT)
771772
# Some variation due to floating point.
772-
assert res['amount_sent_msat'] < 500000000
773+
assert res['amount_sent_msat'] < 800000000
773774
assert res == {'payment_preimage': '00' * 32,
774-
'amount_msat': 500000000,
775+
'amount_msat': 800000000,
775776
'amount_sent_msat': res['amount_sent_msat'],
776777
'failed_parts': 1,
777778
'successful_parts': 1}

0 commit comments

Comments
 (0)