Skip to content

Commit 361deac

Browse files
JssDWtrustyrussell
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 3772a02 commit 361deac

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
@@ -614,11 +614,12 @@ payment_get_excluded_channels(const tal_t *ctx, struct payment *p)
614614
{
615615
struct payment *root = payment_root(p);
616616
struct channel_hint *hint;
617+
struct channel_hint_map_iter iter;
617618
struct short_channel_id_dir *res =
618619
tal_arr(ctx, struct short_channel_id_dir, 0);
619-
for (size_t i = 0; i < tal_count(root->hints->hints); i++) {
620-
hint = &root->hints->hints[i];
621-
620+
for (hint = channel_hint_map_first(root->hints->hints, &iter);
621+
hint;
622+
hint = channel_hint_map_next(root->hints->hints, &iter)) {
622623
if (!hint->enabled)
623624
tal_arr_expand(&res, hint->scid);
624625

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

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

687675
if (dst_is_excluded(gossmap, c, dir, payment_root(p)->excluded_nodes))
@@ -690,8 +678,9 @@ static bool payment_route_check(const struct gossmap *gossmap,
690678
if (dst_is_excluded(gossmap, c, dir, p->temp_exclusion))
691679
return false;
692680

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

@@ -2617,10 +2606,7 @@ local_channel_hints_listpeerchannels(struct command *cmd, const char *buffer,
26172606
* observations, and should re-enable some channels that would
26182607
* otherwise start out as excluded and remain so until
26192608
* forever. */
2620-
2621-
struct channel_hint *hints = payment_root(p)->hints->hints;
2622-
for (size_t i = 0; i < tal_count(hints); i++)
2623-
channel_hint_update(time_now(), &hints[i]);
2609+
channel_hint_set_update(payment_root(p)->hints, time_now());
26242610

26252611
payment_continue(p);
26262612
return command_still_pending(cmd);
@@ -2762,6 +2748,8 @@ static bool routehint_excluded(struct payment *p,
27622748
const struct short_channel_id_dir *chans =
27632749
payment_get_excluded_channels(tmpctx, p);
27642750
const struct channel_hint_set *hints = payment_root(p)->hints;
2751+
struct short_channel_id_dir scidd;
2752+
struct channel_hint *hint;
27652753

27662754
/* Note that we ignore direction here: in theory, we could have
27672755
* found that one direction of a channel is unavailable, but they
@@ -2801,15 +2789,12 @@ static bool routehint_excluded(struct payment *p,
28012789
* know the exact capacity we need to send via this
28022790
* channel, which is greater than the destination.
28032791
*/
2804-
for (size_t j = 0; j < tal_count(hints); j++) {
2805-
if (!short_channel_id_eq(hints->hints[j].scid.scid, r->short_channel_id))
2806-
continue;
2807-
/* We exclude on equality because we set the estimate
2808-
* to the smallest failed attempt. */
2809-
if (amount_msat_greater_eq(needed_capacity,
2810-
hints->hints[j].estimated_capacity))
2811-
return true;
2812-
}
2792+
scidd.scid = r->short_channel_id;
2793+
scidd.dir = node_id_idx(&r->pubkey, p->pay_destination);
2794+
hint = channel_hint_set_find(hints, &scidd);
2795+
if (hint && amount_msat_greater_eq(needed_capacity,
2796+
hint->estimated_capacity))
2797+
return true;
28132798
}
28142799
return false;
28152800
}

plugins/test/run-route-calc.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ struct json_stream *jsonrpc_stream_fail(struct command *cmd UNNEEDED,
264264
/* Generated stub for jsonrpc_stream_success */
265265
struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED)
266266
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
267+
/* Generated stub for memleak_add_helper_ */
268+
void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
269+
const tal_t *)){ }
270+
/* Generated stub for memleak_scan_htable */
271+
void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED)
272+
{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); }
267273
/* Generated stub for notleak_ */
268274
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
269275
{ 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
@@ -267,6 +267,12 @@ struct json_stream *jsonrpc_stream_fail(struct command *cmd UNNEEDED,
267267
/* Generated stub for jsonrpc_stream_success */
268268
struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED)
269269
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
270+
/* Generated stub for memleak_add_helper_ */
271+
void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
272+
const tal_t *)){ }
273+
/* Generated stub for memleak_scan_htable */
274+
void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED)
275+
{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); }
270276
/* Generated stub for notleak_ */
271277
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
272278
{ fprintf(stderr, "notleak_ called!\n"); abort(); }

0 commit comments

Comments
 (0)