mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 01:43:36 +01:00
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.
This commit is contained in:
parent
a6a7dd8f71
commit
cc1362ead3
@ -1,6 +1,34 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include <common/memleak.h>
|
||||||
#include <plugins/channel_hint.h>
|
#include <plugins/channel_hint.h>
|
||||||
|
|
||||||
|
size_t channel_hint_hash(const struct short_channel_id_dir *out)
|
||||||
|
{
|
||||||
|
struct siphash24_ctx ctx;
|
||||||
|
siphash24_init(&ctx, siphash_seed());
|
||||||
|
siphash24_update(&ctx, &out->scid.u64, sizeof(u64));
|
||||||
|
siphash24_update(&ctx, &out->dir, sizeof(int));
|
||||||
|
return siphash24_done(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct short_channel_id_dir *channel_hint_keyof(const struct channel_hint *out)
|
||||||
|
{
|
||||||
|
return &out->scid;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool channel_hint_eq(const struct channel_hint *a,
|
||||||
|
const struct short_channel_id_dir *b)
|
||||||
|
{
|
||||||
|
return short_channel_id_eq(a->scid.scid, b->scid) &&
|
||||||
|
a->scid.dir == b->dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void memleak_help_channel_hint_map(struct htable *memtable,
|
||||||
|
struct channel_hint_map *channel_hints)
|
||||||
|
{
|
||||||
|
memleak_scan_htable(memtable, &channel_hints->raw);
|
||||||
|
}
|
||||||
|
|
||||||
void channel_hint_to_json(const char *name, const struct channel_hint *hint,
|
void channel_hint_to_json(const char *name, const struct channel_hint *hint,
|
||||||
struct json_stream *dest)
|
struct json_stream *dest)
|
||||||
{
|
{
|
||||||
@ -91,15 +119,10 @@ bool channel_hint_update(const struct timeabs now, struct channel_hint *hint)
|
|||||||
amount_msat_greater(capacity, hint->estimated_capacity);
|
amount_msat_greater(capacity, hint->estimated_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct channel_hint *channel_hint_set_find(struct channel_hint_set *self,
|
struct channel_hint *channel_hint_set_find(const struct channel_hint_set *self,
|
||||||
const struct short_channel_id_dir *scidd)
|
const struct short_channel_id_dir *scidd)
|
||||||
{
|
{
|
||||||
for (size_t i=0; i<tal_count(self->hints); i++) {
|
return channel_hint_map_get(self->hints, scidd);
|
||||||
struct channel_hint *hint = &self->hints[i];
|
|
||||||
if (short_channel_id_dir_eq(&hint->scid, scidd))
|
|
||||||
return hint;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* See header */
|
/* See header */
|
||||||
@ -119,7 +142,7 @@ channel_hint_set_add(struct channel_hint_set *self, u32 timestamp,
|
|||||||
old = channel_hint_set_find(self, scidd);
|
old = channel_hint_set_find(self, scidd);
|
||||||
copy = tal_dup(tmpctx, struct channel_hint, old);
|
copy = tal_dup(tmpctx, struct channel_hint, old);
|
||||||
if (old == NULL) {
|
if (old == NULL) {
|
||||||
newhint = tal(tmpctx, struct channel_hint);
|
newhint = tal(self, struct channel_hint);
|
||||||
newhint->enabled = enabled;
|
newhint->enabled = enabled;
|
||||||
newhint->scid = *scidd;
|
newhint->scid = *scidd;
|
||||||
newhint->capacity = capacity;
|
newhint->capacity = capacity;
|
||||||
@ -127,8 +150,8 @@ channel_hint_set_add(struct channel_hint_set *self, u32 timestamp,
|
|||||||
newhint->estimated_capacity = *estimated_capacity;
|
newhint->estimated_capacity = *estimated_capacity;
|
||||||
newhint->local = NULL;
|
newhint->local = NULL;
|
||||||
newhint->timestamp = timestamp;
|
newhint->timestamp = timestamp;
|
||||||
tal_arr_expand(&self->hints, *newhint);
|
channel_hint_map_add(self->hints, newhint);
|
||||||
return &self->hints[tal_count(self->hints) - 1];
|
return newhint;
|
||||||
} else if (old->timestamp <= timestamp) {
|
} else if (old->timestamp <= timestamp) {
|
||||||
/* `local` is kept, since we do not pass in those
|
/* `local` is kept, since we do not pass in those
|
||||||
* annotations here. */
|
* annotations here. */
|
||||||
@ -182,18 +205,24 @@ struct channel_hint *channel_hint_from_json(const tal_t *ctx,
|
|||||||
struct channel_hint_set *channel_hint_set_new(const tal_t *ctx)
|
struct channel_hint_set *channel_hint_set_new(const tal_t *ctx)
|
||||||
{
|
{
|
||||||
struct channel_hint_set *set = tal(ctx, struct channel_hint_set);
|
struct channel_hint_set *set = tal(ctx, struct channel_hint_set);
|
||||||
set->hints = tal_arr(set, struct channel_hint, 0);
|
set->hints = tal(set, struct channel_hint_map);
|
||||||
|
channel_hint_map_init(set->hints);
|
||||||
|
memleak_add_helper(set->hints, memleak_help_channel_hint_map);
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
void channel_hint_set_update(struct channel_hint_set *set,
|
void channel_hint_set_update(struct channel_hint_set *set,
|
||||||
const struct timeabs now)
|
const struct timeabs now)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < tal_count(set->hints); i++)
|
struct channel_hint *hint;
|
||||||
channel_hint_update(time_now(), &set->hints[i]);
|
struct channel_hint_map_iter iter;
|
||||||
|
for (hint = channel_hint_map_first(set->hints, &iter);
|
||||||
|
hint;
|
||||||
|
hint = channel_hint_map_next(set->hints, &iter))
|
||||||
|
channel_hint_update(now, hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t channel_hint_set_count(const struct channel_hint_set *set)
|
size_t channel_hint_set_count(const struct channel_hint_set *set)
|
||||||
{
|
{
|
||||||
return tal_count(set->hints);
|
return channel_hint_map_count(set->hints);
|
||||||
}
|
}
|
||||||
|
@ -45,11 +45,21 @@ struct channel_hint {
|
|||||||
struct amount_msat capacity;
|
struct amount_msat capacity;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
size_t channel_hint_hash(const struct short_channel_id_dir *out);
|
||||||
|
|
||||||
|
const struct short_channel_id_dir *channel_hint_keyof(const struct channel_hint *out);
|
||||||
|
|
||||||
|
bool channel_hint_eq(const struct channel_hint *a,
|
||||||
|
const struct short_channel_id_dir *b);
|
||||||
|
|
||||||
|
HTABLE_DEFINE_TYPE(struct channel_hint, channel_hint_keyof,
|
||||||
|
channel_hint_hash, channel_hint_eq, channel_hint_map)
|
||||||
|
|
||||||
/* A collection of channel_hint instances, allowing us to handle and
|
/* A collection of channel_hint instances, allowing us to handle and
|
||||||
* update them more easily. */
|
* update them more easily. */
|
||||||
struct channel_hint_set {
|
struct channel_hint_set {
|
||||||
/* tal_arr of channel_hints. */
|
/* htable of channel_hints, indexed by scid and direction. */
|
||||||
struct channel_hint *hints;
|
struct channel_hint_map *hints;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool channel_hint_update(const struct timeabs now,
|
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
|
|||||||
/**
|
/**
|
||||||
* Look up a `channel_hint` from a `channel_hint_set` for a scidd.
|
* Look up a `channel_hint` from a `channel_hint_set` for a scidd.
|
||||||
*/
|
*/
|
||||||
struct channel_hint *channel_hint_set_find(struct channel_hint_set *self,
|
struct channel_hint *channel_hint_set_find(const struct channel_hint_set *self,
|
||||||
const struct short_channel_id_dir *scidd);
|
const struct short_channel_id_dir *scidd);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -613,11 +613,12 @@ payment_get_excluded_channels(const tal_t *ctx, struct payment *p)
|
|||||||
{
|
{
|
||||||
struct payment *root = payment_root(p);
|
struct payment *root = payment_root(p);
|
||||||
struct channel_hint *hint;
|
struct channel_hint *hint;
|
||||||
|
struct channel_hint_map_iter iter;
|
||||||
struct short_channel_id_dir *res =
|
struct short_channel_id_dir *res =
|
||||||
tal_arr(ctx, struct short_channel_id_dir, 0);
|
tal_arr(ctx, struct short_channel_id_dir, 0);
|
||||||
for (size_t i = 0; i < tal_count(root->hints->hints); i++) {
|
for (hint = channel_hint_map_first(root->hints->hints, &iter);
|
||||||
hint = &root->hints->hints[i];
|
hint;
|
||||||
|
hint = channel_hint_map_next(root->hints->hints, &iter)) {
|
||||||
if (!hint->enabled)
|
if (!hint->enabled)
|
||||||
tal_arr_expand(&res, hint->scid);
|
tal_arr_expand(&res, hint->scid);
|
||||||
|
|
||||||
@ -640,19 +641,6 @@ static const struct node_id *payment_get_excluded_nodes(const tal_t *ctx,
|
|||||||
return root->excluded_nodes;
|
return root->excluded_nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: This is slow! */
|
|
||||||
static const struct channel_hint *find_hint(const struct channel_hint *hints,
|
|
||||||
struct short_channel_id scid,
|
|
||||||
int dir)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < tal_count(hints); i++) {
|
|
||||||
if (short_channel_id_eq(scid, hints[i].scid.scid)
|
|
||||||
&& dir == hints[i].scid.dir)
|
|
||||||
return &hints[i];
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: This is slow! */
|
/* FIXME: This is slow! */
|
||||||
static bool dst_is_excluded(const struct gossmap *gossmap,
|
static bool dst_is_excluded(const struct gossmap *gossmap,
|
||||||
const struct gossmap_chan *c,
|
const struct gossmap_chan *c,
|
||||||
@ -680,7 +668,7 @@ static bool payment_route_check(const struct gossmap *gossmap,
|
|||||||
struct amount_msat amount,
|
struct amount_msat amount,
|
||||||
struct payment *p)
|
struct payment *p)
|
||||||
{
|
{
|
||||||
struct short_channel_id scid;
|
struct short_channel_id_dir scidd;
|
||||||
const struct channel_hint *hint;
|
const struct channel_hint *hint;
|
||||||
|
|
||||||
if (dst_is_excluded(gossmap, c, dir, payment_root(p)->excluded_nodes))
|
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,
|
|||||||
if (dst_is_excluded(gossmap, c, dir, p->temp_exclusion))
|
if (dst_is_excluded(gossmap, c, dir, p->temp_exclusion))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
scid = gossmap_chan_scid(gossmap, c);
|
scidd.scid = gossmap_chan_scid(gossmap, c);
|
||||||
hint = find_hint(payment_root(p)->hints->hints, scid, dir);
|
scidd.dir = dir;
|
||||||
|
hint = channel_hint_set_find(payment_root(p)->hints, &scidd);
|
||||||
if (!hint)
|
if (!hint)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -2613,10 +2602,7 @@ local_channel_hints_listpeerchannels(struct command *cmd, const char *buffer,
|
|||||||
* observations, and should re-enable some channels that would
|
* observations, and should re-enable some channels that would
|
||||||
* otherwise start out as excluded and remain so until
|
* otherwise start out as excluded and remain so until
|
||||||
* forever. */
|
* forever. */
|
||||||
|
channel_hint_set_update(payment_root(p)->hints, time_now());
|
||||||
struct channel_hint *hints = payment_root(p)->hints->hints;
|
|
||||||
for (size_t i = 0; i < tal_count(hints); i++)
|
|
||||||
channel_hint_update(time_now(), &hints[i]);
|
|
||||||
|
|
||||||
payment_continue(p);
|
payment_continue(p);
|
||||||
return command_still_pending(cmd);
|
return command_still_pending(cmd);
|
||||||
@ -2758,6 +2744,8 @@ static bool routehint_excluded(struct payment *p,
|
|||||||
const struct short_channel_id_dir *chans =
|
const struct short_channel_id_dir *chans =
|
||||||
payment_get_excluded_channels(tmpctx, p);
|
payment_get_excluded_channels(tmpctx, p);
|
||||||
const struct channel_hint_set *hints = payment_root(p)->hints;
|
const struct channel_hint_set *hints = payment_root(p)->hints;
|
||||||
|
struct short_channel_id_dir scidd;
|
||||||
|
struct channel_hint *hint;
|
||||||
|
|
||||||
/* Note that we ignore direction here: in theory, we could have
|
/* Note that we ignore direction here: in theory, we could have
|
||||||
* found that one direction of a channel is unavailable, but they
|
* found that one direction of a channel is unavailable, but they
|
||||||
@ -2797,15 +2785,12 @@ static bool routehint_excluded(struct payment *p,
|
|||||||
* know the exact capacity we need to send via this
|
* know the exact capacity we need to send via this
|
||||||
* channel, which is greater than the destination.
|
* channel, which is greater than the destination.
|
||||||
*/
|
*/
|
||||||
for (size_t j = 0; j < tal_count(hints); j++) {
|
scidd.scid = r->short_channel_id;
|
||||||
if (!short_channel_id_eq(hints->hints[j].scid.scid, r->short_channel_id))
|
scidd.dir = node_id_idx(&r->pubkey, p->pay_destination);
|
||||||
continue;
|
hint = channel_hint_set_find(hints, &scidd);
|
||||||
/* We exclude on equality because we set the estimate
|
if (hint && amount_msat_greater_eq(needed_capacity,
|
||||||
* to the smallest failed attempt. */
|
hint->estimated_capacity))
|
||||||
if (amount_msat_greater_eq(needed_capacity,
|
return true;
|
||||||
hints->hints[j].estimated_capacity))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -272,6 +272,12 @@ struct json_stream *jsonrpc_stream_fail(struct command *cmd UNNEEDED,
|
|||||||
/* Generated stub for jsonrpc_stream_success */
|
/* Generated stub for jsonrpc_stream_success */
|
||||||
struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED)
|
struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED)
|
||||||
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
|
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
|
||||||
|
/* Generated stub for memleak_add_helper_ */
|
||||||
|
void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
|
||||||
|
const tal_t *)){ }
|
||||||
|
/* Generated stub for memleak_scan_htable */
|
||||||
|
void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED)
|
||||||
|
{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); }
|
||||||
/* Generated stub for notleak_ */
|
/* Generated stub for notleak_ */
|
||||||
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
|
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
|
||||||
{ fprintf(stderr, "notleak_ called!\n"); abort(); }
|
{ fprintf(stderr, "notleak_ called!\n"); abort(); }
|
||||||
|
@ -269,6 +269,12 @@ struct json_stream *jsonrpc_stream_fail(struct command *cmd UNNEEDED,
|
|||||||
/* Generated stub for jsonrpc_stream_success */
|
/* Generated stub for jsonrpc_stream_success */
|
||||||
struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED)
|
struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED)
|
||||||
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
|
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
|
||||||
|
/* Generated stub for memleak_add_helper_ */
|
||||||
|
void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
|
||||||
|
const tal_t *)){ }
|
||||||
|
/* Generated stub for memleak_scan_htable */
|
||||||
|
void memleak_scan_htable(struct htable *memtable UNNEEDED, const struct htable *ht UNNEEDED)
|
||||||
|
{ fprintf(stderr, "memleak_scan_htable called!\n"); abort(); }
|
||||||
/* Generated stub for notleak_ */
|
/* Generated stub for notleak_ */
|
||||||
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
|
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
|
||||||
{ fprintf(stderr, "notleak_ called!\n"); abort(); }
|
{ fprintf(stderr, "notleak_ called!\n"); abort(); }
|
||||||
|
Loading…
Reference in New Issue
Block a user