routing: pre-allocate the distance map to an estimated node count

Pre-sizing these structures avoids a lot of map resizing, which causes
copies and rehashing of entries. We mostly know that the map won't
exceed that size, and it doesn't affect memory usage in any significant
way.
This commit is contained in:
Juan Pablo Civile 2019-08-23 12:27:02 -03:00
parent 2141713936
commit 3e60a23632
3 changed files with 23 additions and 17 deletions

@ -50,9 +50,10 @@ type distanceHeap struct {
// newDistanceHeap initializes a new distance heap. This is required because
// we must initialize the pubkeyIndices map for path-finding optimizations.
func newDistanceHeap() distanceHeap {
func newDistanceHeap(numNodes int) distanceHeap {
distHeap := distanceHeap{
pubkeyIndices: make(map[route.Vertex]int),
pubkeyIndices: make(map[route.Vertex]int, numNodes),
nodes: make([]nodeWithDist, 0, numNodes),
}
return distHeap

@ -17,7 +17,7 @@ func TestHeapOrdering(t *testing.T) {
// First, create a blank heap, we'll use this to push on randomly
// generated items.
nodeHeap := newDistanceHeap()
nodeHeap := newDistanceHeap(0)
prand.Seed(1)

@ -37,6 +37,11 @@ const (
// some effect with smaller time lock values. The value may need
// tweaking and/or be made configurable in the future.
RiskFactorBillionths = 15
// estimatedNodeCount is used to preallocate the path finding structures
// to avoid resizing and copies. It should be number on the same order as
// the number of active nodes in the network.
estimatedNodeCount = 10000
)
// pathFinder defines the interface of a path finding algorithm.
@ -320,14 +325,6 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
defer tx.Rollback()
}
// First we'll initialize an empty heap which'll help us to quickly
// locate the next edge we should visit next during our graph
// traversal.
nodeHeap := newDistanceHeap()
// Holds the current best distance for a given node.
distance := make(map[route.Vertex]nodeWithDist)
if r.DestPayloadTLV {
// Check if the target has TLV enabled
@ -352,6 +349,19 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
}
}
// First we'll initialize an empty heap which'll help us to quickly
// locate the next edge we should visit next during our graph
// traversal.
nodeHeap := newDistanceHeap(estimatedNodeCount)
// Holds the current best distance for a given node.
distance := make(map[route.Vertex]nodeWithDist, estimatedNodeCount)
// We'll use this map as a series of "next" hop pointers. So to get
// from `Vertex` to the target node, we'll take the edge that it's
// mapped to within `next`.
next := make(map[route.Vertex]*channeldb.ChannelEdgePolicy, estimatedNodeCount)
additionalEdgesWithSrc := make(map[route.Vertex][]*edgePolicyWithSource)
for vertex, outgoingEdgePolicies := range g.additionalEdges {
@ -385,11 +395,6 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
probability: 1,
}
// We'll use this map as a series of "next" hop pointers. So to get
// from `Vertex` to the target node, we'll take the edge that it's
// mapped to within `next`.
next := make(map[route.Vertex]*channeldb.ChannelEdgePolicy)
// processEdge is a helper closure that will be used to make sure edges
// satisfy our specific requirements.
processEdge := func(fromVertex route.Vertex, bandwidth lnwire.MilliSatoshi,
@ -659,7 +664,7 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
// Use the nextHop map to unravel the forward path from source to
// target.
pathEdges := make([]*channeldb.ChannelEdgePolicy, 0, len(next))
var pathEdges []*channeldb.ChannelEdgePolicy
currentNode := source
for currentNode != target { // TODO(roasbeef): assumes no cycles
// Determine the next hop forward using the next map.