Browse Source

gossipd: speed Dijkstra a little.

Our uintmap can be a little slow with all the reallocation, so leave
NULL entries and walk to find the first one.  Since we don't clean
them up, keep a cache of where the min non-all-NULL value is in the
heap.

It's clearer benefit on really large tests, so here's 1M nodes:

Comparison using gossipd/test/run-bench-find_route 1000000 10:

Before:
	10 (10 succeeded) routes in 1000000 nodes in 91995 msec (9199532898 nanoseconds per route)

After:
	10 (10 succeeded) routes in 1000000 nodes in 20605 msec (2060539287 nanoseconds per route)

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
pr-2587
Rusty Russell 6 years ago
parent
commit
f8ffae837d
  1. 144
      gossipd/routing.c

144
gossipd/routing.c

@ -428,9 +428,16 @@ struct chan *new_chan(struct routing_state *rstate,
/* We hack a multimap into a uintmap to implement a minheap by cost. /* We hack a multimap into a uintmap to implement a minheap by cost.
* This is relatively inefficient, containing an array for each cost * This is relatively inefficient, containing an array for each cost
* value, assuming there aren't too many at same cost. FIXME: * value, assuming there aren't too many at same cost.
* implement an array heap */ *
typedef UINTMAP(struct node **) unvisited_t; * We further optimize by never freeing or shrinking these entries,
* but delete by replacing with NULL. This means that we cache the
* lowest index which actually contains something, since others may
* contain empty arrays. */
struct unvisited {
u64 min_index;
UINTMAP(struct node **) map;
};
static void clear_bfg(struct node_map *nodes) static void clear_bfg(struct node_map *nodes)
{ {
@ -597,8 +604,31 @@ static bool hc_is_routable(struct routing_state *rstate,
&& !is_chan_local_disabled(rstate, chan); && !is_chan_local_disabled(rstate, chan);
} }
static void unvisited_add(struct unvisited *unvisited, struct amount_msat cost,
struct node **arr)
{
u64 idx = cost.millisatoshis; /* Raw: uintmap needs u64 index */
if (idx < unvisited->min_index) {
assert(idx); /* We don't allow sending 0 satoshis */
unvisited->min_index = idx - 1;
}
uintmap_add(&unvisited->map, idx, arr);
}
static struct node **unvisited_get(const struct unvisited *unvisited,
struct amount_msat cost)
{
return uintmap_get(&unvisited->map, cost.millisatoshis); /* Raw: uintmap */
}
static struct node **unvisited_del(struct unvisited *unvisited,
struct amount_msat cost)
{
return uintmap_del(&unvisited->map, cost.millisatoshis); /* Raw: uintmap */
}
static bool is_unvisited(const struct node *node, static bool is_unvisited(const struct node *node,
const unvisited_t *unvisited) const struct unvisited *unvisited)
{ {
struct node **arr; struct node **arr;
struct amount_msat cost; struct amount_msat cost;
@ -617,7 +647,7 @@ static bool is_unvisited(const struct node *node,
return false; return false;
} }
arr = uintmap_get(unvisited, cost.millisatoshis); arr = unvisited_get(unvisited, cost);
for (size_t i = 0; i < tal_count(arr); i++) { for (size_t i = 0; i < tal_count(arr); i++) {
if (arr[i] == node) if (arr[i] == node)
return true; return true;
@ -625,39 +655,24 @@ static bool is_unvisited(const struct node *node,
return false; return false;
} }
static void unvisited_add(unvisited_t *unvisited, struct amount_msat cost, static void unvisited_del_node(struct unvisited *unvisited,
struct node **arr)
{
uintmap_add(unvisited, cost.millisatoshis, arr); /* Raw: uintmap */
}
static struct node **unvisited_del(unvisited_t *unvisited,
struct amount_msat cost)
{
return uintmap_del(unvisited, cost.millisatoshis); /* Raw: uintmap */
}
static void unvisited_del_node(unvisited_t *unvisited,
struct amount_msat cost, struct amount_msat cost,
const struct node *node) const struct node *node)
{ {
size_t i;
struct node **arr; struct node **arr;
/* Remove may reallocate, so we delete and re-add. */ arr = unvisited_get(unvisited, cost);
arr = unvisited_del(unvisited, cost); for (size_t i = 0; i < tal_count(arr); i++) {
for (i = 0; arr[i] != node; i++) if (arr[i] == node) {
assert(i < tal_count(arr)); arr[i] = NULL;
return;
tal_arr_remove(&arr, i); }
if (tal_count(arr) == 0) }
tal_free(arr); abort();
else
unvisited_add(unvisited, cost, arr);
} }
static void adjust_unvisited(struct node *node, static void adjust_unvisited(struct node *node,
unvisited_t *unvisited, struct unvisited *unvisited,
struct amount_msat cost_before, struct amount_msat cost_before,
struct amount_msat total, struct amount_msat total,
struct amount_msat risk, struct amount_msat risk,
@ -674,17 +689,33 @@ static void adjust_unvisited(struct node *node,
node->dijkstra.risk = risk; node->dijkstra.risk = risk;
/* Update map of unvisited nodes */ /* Update map of unvisited nodes */
arr = unvisited_del(unvisited, cost_after); arr = unvisited_get(unvisited, cost_after);
if (!arr) { if (arr) {
struct node **old_arr;
/* Try for empty slot */
for (size_t i = 0; i < tal_count(arr); i++) {
if (arr[i] == NULL) {
arr[i] = node;
return;
}
}
/* Nope, expand */
old_arr = arr;
tal_arr_expand(&arr, node);
if (arr == old_arr)
return;
/* Realloc moved it; del and add again. */
unvisited_del(unvisited, cost_after);
} else {
arr = tal_arr(unvisited, struct node *, 1); arr = tal_arr(unvisited, struct node *, 1);
arr[0] = node; arr[0] = node;
} else }
tal_arr_expand(&arr, node);
unvisited_add(unvisited, cost_after, arr); unvisited_add(unvisited, cost_after, arr);
} }
static void remove_unvisited(struct node *node, unvisited_t *unvisited) static void remove_unvisited(struct node *node, struct unvisited *unvisited)
{ {
struct amount_msat cost; struct amount_msat cost;
@ -704,7 +735,7 @@ static void remove_unvisited(struct node *node, unvisited_t *unvisited)
static void update_unvisited_neighbors(struct routing_state *rstate, static void update_unvisited_neighbors(struct routing_state *rstate,
struct node *cur, struct node *cur,
double riskfactor, double riskfactor,
unvisited_t *unvisited) struct unvisited *unvisited)
{ {
struct chan_map_iter i; struct chan_map_iter i;
struct chan *chan; struct chan *chan;
@ -746,20 +777,23 @@ static void update_unvisited_neighbors(struct routing_state *rstate,
} }
} }
static struct node *first_unvisited(unvisited_t *unvisited) static struct node *first_unvisited(struct unvisited *unvisited)
{ {
u64 idx; struct node **arr;
struct node **arr = uintmap_first(unvisited, &idx);
while ((arr = uintmap_after(&unvisited->map, &unvisited->min_index))) {
for (size_t i = 0; i < tal_count(arr); i++)
if (arr[i])
return arr[i];
}
if (arr)
return arr[0];
return NULL; return NULL;
} }
static void dijkstra(struct routing_state *rstate, static void dijkstra(struct routing_state *rstate,
const struct node *dst, const struct node *dst,
double riskfactor, double riskfactor,
unvisited_t *unvisited) struct unvisited *unvisited)
{ {
struct node *cur; struct node *cur;
@ -854,18 +888,19 @@ static struct chan **build_route(const tal_t *ctx,
return route; return route;
} }
static unvisited_t *dijkstra_prepare(const tal_t *ctx, static struct unvisited *dijkstra_prepare(const tal_t *ctx,
struct routing_state *rstate, struct routing_state *rstate,
struct node *src, struct node *src,
struct amount_msat msat) struct amount_msat msat)
{ {
struct node_map_iter it; struct node_map_iter it;
unvisited_t *unvisited; struct unvisited *unvisited;
struct node *n; struct node *n;
struct node **arr; struct node **arr;
unvisited = tal(ctx, unvisited_t); unvisited = tal(tmpctx, struct unvisited);
uintmap_init(unvisited); uintmap_init(&unvisited->map);
unvisited->min_index = UINT64_MAX;
/* Reset all the information. */ /* Reset all the information. */
for (n = node_map_first(rstate->nodes, &it); for (n = node_map_first(rstate->nodes, &it);
@ -887,15 +922,15 @@ static unvisited_t *dijkstra_prepare(const tal_t *ctx,
return unvisited; return unvisited;
} }
static void dijkstra_cleanup(unvisited_t *unvisited) static void dijkstra_cleanup(struct unvisited *unvisited)
{ {
struct node **arr; struct node **arr;
u64 idx; u64 idx;
/* uintmap uses malloc, so manual cleaning needed */ /* uintmap uses malloc, so manual cleaning needed */
while ((arr = uintmap_first(unvisited, &idx)) != NULL) { while ((arr = uintmap_first(&unvisited->map, &idx)) != NULL) {
tal_free(arr); tal_free(arr);
uintmap_del(unvisited, idx); uintmap_del(&unvisited->map, idx);
} }
tal_free(unvisited); tal_free(unvisited);
} }
@ -911,7 +946,7 @@ find_route_dijkstra(const tal_t *ctx, struct routing_state *rstate,
struct amount_msat *fee) struct amount_msat *fee)
{ {
struct node *src, *dst; struct node *src, *dst;
unvisited_t *unvisited; struct unvisited *unvisited;
/* Note: we map backwards, since we know the amount of satoshi we want /* Note: we map backwards, since we know the amount of satoshi we want
* at the end, and need to derive how much we need to send. */ * at the end, and need to derive how much we need to send. */
@ -2199,6 +2234,9 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate,
base_seed.u.u64[0] = base_seed.u.u64[1] = seed; base_seed.u.u64[0] = base_seed.u.u64[1] = seed;
if (amount_msat_eq(msat, AMOUNT_MSAT(0)))
return NULL;
/* Temporarily set excluded channels' capacity to zero. */ /* Temporarily set excluded channels' capacity to zero. */
for (size_t i = 0; i < tal_count(excluded); i++) { for (size_t i = 0; i < tal_count(excluded); i++) {
struct chan *chan = get_channel(rstate, &excluded[i].scid); struct chan *chan = get_channel(rstate, &excluded[i].scid);

Loading…
Cancel
Save