Skip to content

Commit cc1362e

Browse files
JssDWtcdecker
authored andcommitted
libplugin-pay: use map for channel hints
For nodes with many channels this is a tremendous improvement in pay performance. PR #7611 improves payment performance from 15 seconds to 13.5 seconds on one of our nodes. This commit improves payment performance from 13.5 seconds to 5.7 seconds. Changelog-Fixed: Improved pathfinding speed for large nodes.
1 parent a6a7dd8 commit cc1362e

File tree

5 files changed

+85
-49
lines changed

5 files changed

+85
-49
lines changed

plugins/channel_hint.c

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,34 @@
11
#include "config.h"
2+
#include <common/memleak.h>
23
#include <plugins/channel_hint.h>
34

5+
size_t channel_hint_hash(const struct short_channel_id_dir *out)
6+
{
7+
struct siphash24_ctx ctx;
8+
siphash24_init(&ctx, siphash_seed());
9+
siphash24_update(&ctx, &out->scid.u64, sizeof(u64));
10+
siphash24_update(&ctx, &out->dir, sizeof(int));
11+
return siphash24_done(&ctx);
12+
}
13+
14+
const struct short_channel_id_dir *channel_hint_keyof(const struct channel_hint *out)
15+
{
16+
return &out->scid;
17+
}
18+
19+
bool channel_hint_eq(const struct channel_hint *a,
20+
const struct short_channel_id_dir *b)
21+
{
22+
return short_channel_id_eq(a->scid.scid, b->scid) &&
23+
a->scid.dir == b->dir;
24+
}
25+
26+
static void memleak_help_channel_hint_map(struct htable *memtable,
27+
struct channel_hint_map *channel_hints)
28+
{
29+
memleak_scan_htable(memtable, &channel_hints->raw);
30+
}
31+
432
void channel_hint_to_json(const char *name, const struct channel_hint *hint,
533
struct json_stream *dest)
634
{
@@ -91,15 +119,10 @@ bool channel_hint_update(const struct timeabs now, struct channel_hint *hint)
91119
amount_msat_greater(capacity, hint->estimated_capacity);
92120
}
93121

94-
struct channel_hint *channel_hint_set_find(struct channel_hint_set *self,
122+
struct channel_hint *channel_hint_set_find(const struct channel_hint_set *self,
95123
const struct short_channel_id_dir *scidd)
96124
{
97-
for (size_t i=0; i<tal_count(self->hints); i++) {
98-
struct channel_hint *hint = &self->hints[i];
99-
if (short_channel_id_dir_eq(&hint->scid, scidd))
100-
return hint;
101-
}
102-
return NULL;
125+
return channel_hint_map_get(self->hints, scidd);
103126
}
104127

105128
/* See header */
@@ -119,16 +142,16 @@ channel_hint_set_add(struct channel_hint_set *self, u32 timestamp,
119142
old = channel_hint_set_find(self, scidd);
120143
copy = tal_dup(tmpctx, struct channel_hint, old);
121144
if (old == NULL) {
122-
newhint = tal(tmpctx, struct channel_hint);
145+
newhint = tal(self, struct channel_hint);
123146
newhint->enabled = enabled;
124147
newhint->scid = *scidd;
125148
newhint->capacity = capacity;
126149
if (estimated_capacity != NULL)
127150
newhint->estimated_capacity = *estimated_capacity;
128151
newhint->local = NULL;
129152
newhint->timestamp = timestamp;
130-
tal_arr_expand(&self->hints, *newhint);
131-
return &self->hints[tal_count(self->hints) - 1];
153+
channel_hint_map_add(self->hints, newhint);
154+
return newhint;
132155
} else if (old->timestamp <= timestamp) {
133156
/* `local` is kept, since we do not pass in those
134157
* annotations here. */
@@ -182,18 +205,24 @@ struct channel_hint *channel_hint_from_json(const tal_t *ctx,
182205
struct channel_hint_set *channel_hint_set_new(const tal_t *ctx)
183206
{
184207
struct channel_hint_set *set = tal(ctx, struct channel_hint_set);
185-
set->hints = tal_arr(set, struct channel_hint, 0);
208+
set->hints = tal(set, struct channel_hint_map);
209+
channel_hint_map_init(set->hints);
210+
memleak_add_helper(set->hints, memleak_help_channel_hint_map);
186211
return set;
187212
}
188213

189214
void channel_hint_set_update(struct channel_hint_set *set,
190215
const struct timeabs now)
191216
{
192-
for (size_t i = 0; i < tal_count(set->hints); i++)
193-
channel_hint_update(time_now(), &set->hints[i]);
217+
struct channel_hint *hint;
218+
struct channel_hint_map_iter iter;
219+
for (hint = channel_hint_map_first(set->hints, &iter);
220+
hint;
221+
hint = channel_hint_map_next(set->hints, &iter))
222+
channel_hint_update(now, hint);
194223
}
195224

196225
size_t channel_hint_set_count(const struct channel_hint_set *set)
197226
{
198-
return tal_count(set->hints);
227+
return channel_hint_map_count(set->hints);
199228
}

plugins/channel_hint.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,21 @@ struct channel_hint {
4545
struct amount_msat capacity;
4646
};
4747

48+
size_t channel_hint_hash(const struct short_channel_id_dir *out);
49+
50+
const struct short_channel_id_dir *channel_hint_keyof(const struct channel_hint *out);
51+
52+
bool channel_hint_eq(const struct channel_hint *a,
53+
const struct short_channel_id_dir *b);
54+
55+
HTABLE_DEFINE_TYPE(struct channel_hint, channel_hint_keyof,
56+
channel_hint_hash, channel_hint_eq, channel_hint_map)
57+
4858
/* A collection of channel_hint instances, allowing us to handle and
4959
* update them more easily. */
5060
struct channel_hint_set {
51-
/* tal_arr of channel_hints. */
52-
struct channel_hint *hints;
61+
/* htable of channel_hints, indexed by scid and direction. */
62+
struct channel_hint_map *hints;
5363
};
5464

5565
bool channel_hint_update(const struct timeabs now,
@@ -70,7 +80,7 @@ void channel_hint_set_update(struct channel_hint_set *set, const struct timeabs
7080
/**
7181
* Look up a `channel_hint` from a `channel_hint_set` for a scidd.
7282
*/
73-
struct channel_hint *channel_hint_set_find(struct channel_hint_set *self,
83+
struct channel_hint *channel_hint_set_find(const struct channel_hint_set *self,
7484
const struct short_channel_id_dir *scidd);
7585

7686
/**

plugins/libplugin-pay.c

Lines changed: 17 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -613,11 +613,12 @@ payment_get_excluded_channels(const tal_t *ctx, struct payment *p)
613613
{
614614
struct payment *root = payment_root(p);
615615
struct channel_hint *hint;
616+
struct channel_hint_map_iter iter;
616617
struct short_channel_id_dir *res =
617618
tal_arr(ctx, struct short_channel_id_dir, 0);
618-
for (size_t i = 0; i < tal_count(root->hints->hints); i++) {
619-
hint = &root->hints->hints[i];
620-
619+
for (hint = channel_hint_map_first(root->hints->hints, &iter);
620+
hint;
621+
hint = channel_hint_map_next(root->hints->hints, &iter)) {
621622
if (!hint->enabled)
622623
tal_arr_expand(&res, hint->scid);
623624

@@ -640,19 +641,6 @@ static const struct node_id *payment_get_excluded_nodes(const tal_t *ctx,
640641
return root->excluded_nodes;
641642
}
642643

643-
/* FIXME: This is slow! */
644-
static const struct channel_hint *find_hint(const struct channel_hint *hints,
645-
struct short_channel_id scid,
646-
int dir)
647-
{
648-
for (size_t i = 0; i < tal_count(hints); i++) {
649-
if (short_channel_id_eq(scid, hints[i].scid.scid)
650-
&& dir == hints[i].scid.dir)
651-
return &hints[i];
652-
}
653-
return NULL;
654-
}
655-
656644
/* FIXME: This is slow! */
657645
static bool dst_is_excluded(const struct gossmap *gossmap,
658646
const struct gossmap_chan *c,
@@ -680,7 +668,7 @@ static bool payment_route_check(const struct gossmap *gossmap,
680668
struct amount_msat amount,
681669
struct payment *p)
682670
{
683-
struct short_channel_id scid;
671+
struct short_channel_id_dir scidd;
684672
const struct channel_hint *hint;
685673

686674
if (dst_is_excluded(gossmap, c, dir, payment_root(p)->excluded_nodes))
@@ -689,8 +677,9 @@ static bool payment_route_check(const struct gossmap *gossmap,
689677
if (dst_is_excluded(gossmap, c, dir, p->temp_exclusion))
690678
return false;
691679

692-
scid = gossmap_chan_scid(gossmap, c);
693-
hint = find_hint(payment_root(p)->hints->hints, scid, dir);
680+
scidd.scid = gossmap_chan_scid(gossmap, c);
681+
scidd.dir = dir;
682+
hint = channel_hint_set_find(payment_root(p)->hints, &scidd);
694683
if (!hint)
695684
return true;
696685

@@ -2613,10 +2602,7 @@ local_channel_hints_listpeerchannels(struct command *cmd, const char *buffer,
26132602
* observations, and should re-enable some channels that would
26142603
* otherwise start out as excluded and remain so until
26152604
* forever. */
2616-
2617-
struct channel_hint *hints = payment_root(p)->hints->hints;
2618-
for (size_t i = 0; i < tal_count(hints); i++)
2619-
channel_hint_update(time_now(), &hints[i]);
2605+
channel_hint_set_update(payment_root(p)->hints, time_now());
26202606

26212607
payment_continue(p);
26222608
return command_still_pending(cmd);
@@ -2758,6 +2744,8 @@ static bool routehint_excluded(struct payment *p,
27582744
const struct short_channel_id_dir *chans =
27592745
payment_get_excluded_channels(tmpctx, p);
27602746
const struct channel_hint_set *hints = payment_root(p)->hints;
2747+
struct short_channel_id_dir scidd;
2748+
struct channel_hint *hint;
27612749

27622750
/* Note that we ignore direction here: in theory, we could have
27632751
* found that one direction of a channel is unavailable, but they
@@ -2797,15 +2785,12 @@ static bool routehint_excluded(struct payment *p,
27972785
* know the exact capacity we need to send via this
27982786
* channel, which is greater than the destination.
27992787
*/
2800-
for (size_t j = 0; j < tal_count(hints); j++) {
2801-
if (!short_channel_id_eq(hints->hints[j].scid.scid, r->short_channel_id))
2802-
continue;
2803-
/* We exclude on equality because we set the estimate
2804-
* to the smallest failed attempt. */
2805-
if (amount_msat_greater_eq(needed_capacity,
2806-
hints->hints[j].estimated_capacity))
2807-
return true;
2808-
}
2788+
scidd.scid = r->short_channel_id;
2789+
scidd.dir = node_id_idx(&r->pubkey, p->pay_destination);
2790+
hint = channel_hint_set_find(hints, &scidd);
2791+
if (hint && amount_msat_greater_eq(needed_capacity,
2792+
hint->estimated_capacity))
2793+
return true;
28092794
}
28102795
return false;
28112796
}

plugins/test/run-route-calc.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,12 @@ struct json_stream *jsonrpc_stream_fail(struct command *cmd UNNEEDED,
272272
/* Generated stub for jsonrpc_stream_success */
273273
struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED)
274274
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
275+
/* Generated stub for memleak_add_helper_ */
276+
void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
277+
const tal_t *)){ }
278+
/* Generated stub for memleak_scan_htable */
279+
void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED)
280+
{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); }
275281
/* Generated stub for notleak_ */
276282
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
277283
{ fprintf(stderr, "notleak_ called!\n"); abort(); }

plugins/test/run-route-overlong.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,12 @@ struct json_stream *jsonrpc_stream_fail(struct command *cmd UNNEEDED,
269269
/* Generated stub for jsonrpc_stream_success */
270270
struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED)
271271
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
272+
/* Generated stub for memleak_add_helper_ */
273+
void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
274+
const tal_t *)){ }
275+
/* Generated stub for memleak_scan_htable */
276+
void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED)
277+
{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); }
272278
/* Generated stub for notleak_ */
273279
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
274280
{ fprintf(stderr, "notleak_ called!\n"); abort(); }

0 commit comments

Comments
 (0)