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>
This commit is contained in:
Lagrang3
2025-07-15 07:56:31 +01:00
committed by Rusty Russell
parent b7f3d7bcc4
commit 420cff9350
3 changed files with 24 additions and 18 deletions

View File

@@ -969,10 +969,15 @@ struct flow **minflow(const tal_t *ctx,
params->source = source;
params->target = target;
params->amount = amount;
params->accuracy = AMOUNT_MSAT(1000);
/* FIXME: params->accuracy = amount_msat_max(amount_msat_div(amount,
* 1000), AMOUNT_MSAT(1));
/* -> We reduce the granularity of the flow by limiting the subdivision
* of the payment amount into 1000 units of flow. That reduces the
* computational burden for algorithms that depend on it, eg. "capacity
* scaling" and "successive shortest path".
* -> Using Ceil operation instead of Floor so that
* accuracy x 1000 >= amount
* */
params->accuracy =
amount_msat_max(AMOUNT_MSAT(1), amount_msat_div_ceil(amount, 1000));
// template the channel partition into linear arcs
params->cap_fraction[0]=0;

View File

@@ -1204,10 +1204,10 @@ def test_real_data(node_factory, bitcoind):
# CI, it's slow.
if SLOW_MACHINE:
limit = 25
expected = (5, 25, 1567535, 142772, 91)
expected = (6, 25, 1568821, 143649, 91)
else:
limit = 100
expected = (9, 96, 6563767, 629671, 91)
expected = (9, 96, 6565467, 630668, 91)
fees = {}
for n in range(0, limit):
@@ -1324,7 +1324,7 @@ def test_real_biases(node_factory, bitcoind):
expected = ({1: 6, 2: 6, 4: 7, 8: 12, 16: 14, 32: 19, 64: 25, 100: 25}, 0)
else:
limit = 100
expected = ({1: 22, 2: 25, 4: 36, 8: 52, 16: 69, 32: 80, 64: 96, 100: 96}, 0)
expected = ({1: 22, 2: 25, 4: 36, 8: 53, 16: 69, 32: 80, 64: 96, 100: 96}, 0)
l1.rpc.askrene_create_layer('biases')
num_changed = {}

View File

@@ -671,7 +671,7 @@ def test_xpay_bolt12_no_mpp(node_factory, chainparams, deprecations):
# Amount needs to be enought that it bothers splitting, but not
# so much that it can't pay without mpp.
AMOUNT = 500000000
AMOUNT = 800000000
# l2 will advertize mpp, l3 won't.
l2offer = l2.rpc.offer(AMOUNT, 'test_xpay_bolt12_no_mpp')
@@ -686,10 +686,11 @@ def test_xpay_bolt12_no_mpp(node_factory, chainparams, deprecations):
assert ret['failed_parts'] == 0
if deprecations:
assert ret['successful_parts'] == 2
assert ret['amount_sent_msat'] == AMOUNT + AMOUNT // 100000 + 2
else:
assert ret['successful_parts'] == 1
assert ret['amount_sent_msat'] == AMOUNT + AMOUNT // 100000 + 1
assert ret['amount_msat'] == AMOUNT
assert ret['amount_sent_msat'] == AMOUNT + AMOUNT // 100000 + 1
def test_xpay_slow_mode(node_factory, bitcoind):
@@ -706,18 +707,18 @@ def test_xpay_slow_mode(node_factory, bitcoind):
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 10)
# First try an MPP which fails
inv = l5.rpc.invoice(500000000, 'test_xpay_slow_mode_fail', 'test_xpay_slow_mode_fail', preimage='01' * 32)['bolt11']
inv = l5.rpc.invoice(800000000, 'test_xpay_slow_mode_fail', 'test_xpay_slow_mode_fail', preimage='01' * 32)['bolt11']
l5.rpc.delinvoice('test_xpay_slow_mode_fail', status='unpaid')
with pytest.raises(RpcError, match=r"Destination said it doesn't know invoice: incorrect_or_unknown_payment_details"):
l1.rpc.xpay(inv)
# Now a successful one
inv = l5.rpc.invoice(500000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
inv = l5.rpc.invoice(800000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
assert l1.rpc.xpay(inv) == {'payment_preimage': '00' * 32,
'amount_msat': 500000000,
'amount_sent_msat': 500010002,
'amount_msat': 800000000,
'amount_sent_msat': 800016004,
'failed_parts': 0,
'successful_parts': 2}
@@ -739,7 +740,7 @@ def test_fail_after_success(node_factory, bitcoind, executor, slow_mode):
bitcoind.generate_block(5)
wait_for(lambda: len(l1.rpc.listchannels()['channels']) == 10)
inv = l5.rpc.invoice(500000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
inv = l5.rpc.invoice(800000000, 'test_xpay_slow_mode', 'test_xpay_slow_mode', preimage='00' * 32)['bolt11']
fut = executor.submit(l1.rpc.xpay, invstring=inv, retry_for=0)
# 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):
# Normally, we return as soon as first part succeeds.
if slow_mode is False:
assert fut.result(TIMEOUT) == {'payment_preimage': '00' * 32,
'amount_msat': 500000000,
'amount_sent_msat': 500010002,
'amount_msat': 800000000,
'amount_sent_msat': 800016004,
'failed_parts': 0,
'successful_parts': 2}
@@ -763,15 +764,15 @@ def test_fail_after_success(node_factory, bitcoind, executor, slow_mode):
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\)")
# Could be either way around, check both
line = l1.daemon.is_in_log(r"UNUSUAL.*Destination accepted partial payment, failed a part")
assert re.search(r'but accepted only 32000msat of 500000000msat\. Winning\?!', line) or re.search(r'but accepted only 499968000msat of 500000000msat\. Winning\?!', line)
assert re.search(r'but accepted only .* of 800000000msat\. Winning\?!', line)
if slow_mode is True:
# Now it succeeds, but notes that it only sent one part!
res = fut.result(TIMEOUT)
# Some variation due to floating point.
assert res['amount_sent_msat'] < 500000000
assert res['amount_sent_msat'] < 800000000
assert res == {'payment_preimage': '00' * 32,
'amount_msat': 500000000,
'amount_msat': 800000000,
'amount_sent_msat': res['amount_sent_msat'],
'failed_parts': 1,
'successful_parts': 1}