routing: avoid walking all nodes for path finding if we don't need to

Calling `ForEachNode` hits the DB, and allocates and parses every node
in the graph. Walking the channels also loads nodes from the DB, so this
meant that each node was read/parsed/allocated several times per run.

This reduces runtime by ~10ms and memory usage by ~4mb.
This commit is contained in:
Juan Pablo Civile 2019-08-20 13:27:03 -03:00
parent 5389161162
commit 2141713936

@ -6,6 +6,7 @@ import (
"math" "math"
"time" "time"
"github.com/btcsuite/btcd/btcec"
"github.com/coreos/bbolt" "github.com/coreos/bbolt"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
@ -324,56 +325,35 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
// traversal. // traversal.
nodeHeap := newDistanceHeap() nodeHeap := newDistanceHeap()
// For each node in the graph, we create an entry in the distance map // Holds the current best distance for a given node.
// for the node set with a distance of "infinity". graph.ForEachNode
// also returns the source node, so there is no need to add the source
// node explicitly.
distance := make(map[route.Vertex]nodeWithDist) distance := make(map[route.Vertex]nodeWithDist)
if err := g.graph.ForEachNode(tx, func(_ *bbolt.Tx,
node *channeldb.LightningNode) error { if r.DestPayloadTLV {
// TODO(roasbeef): with larger graph can just use disk seeks // Check if the target has TLV enabled
// with a visited map
vertex := route.Vertex(node.PubKeyBytes) targetKey, err := btcec.ParsePubKey(target[:], btcec.S256())
distance[vertex] = nodeWithDist{ if err != nil {
dist: infinity, return nil, err
node: route.Vertex(node.PubKeyBytes),
} }
// If we don't have any features for this node, then we can targetNode, err := g.graph.FetchLightningNode(targetKey)
// stop here. if err != nil {
if node.Features == nil || !r.DestPayloadTLV { return nil, err
return nil
} }
// We only need to perform this check for the final node, so we if targetNode.Features != nil {
// can exit here if this isn't them. supportsTLV := targetNode.Features.HasFeature(
if vertex != target {
return nil
}
// If we have any records for the final hop, then we'll check
// not to ensure that they are actually able to interpret them.
supportsTLV := node.Features.HasFeature(
lnwire.TLVOnionPayloadOptional, lnwire.TLVOnionPayloadOptional,
) )
if !supportsTLV { if !supportsTLV {
return fmt.Errorf("destination hop doesn't " + return nil, fmt.Errorf("destination hop doesn't " +
"understand new TLV paylods") "understand new TLV paylods")
} }
}
return nil
}); err != nil {
return nil, err
} }
additionalEdgesWithSrc := make(map[route.Vertex][]*edgePolicyWithSource) additionalEdgesWithSrc := make(map[route.Vertex][]*edgePolicyWithSource)
for vertex, outgoingEdgePolicies := range g.additionalEdges { for vertex, outgoingEdgePolicies := range g.additionalEdges {
// We'll also include all the nodes found within the additional
// edges that are not known to us yet in the distance map.
distance[vertex] = nodeWithDist{
dist: infinity,
node: vertex,
}
// Build reverse lookup to find incoming edges. Needed because // Build reverse lookup to find incoming edges. Needed because
// search is taken place from target to source. // search is taken place from target to source.
@ -391,11 +371,11 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
} }
// We can't always assume that the end destination is publicly // We can't always assume that the end destination is publicly
// advertised to the network and included in the graph.ForEachNode call // advertised to the network so we'll manually include the target node.
// above, so we'll manually include the target node. The target node // The target node charges no fee. Distance is set to 0, because this
// charges no fee. Distance is set to 0, because this is the starting // is the starting point of the graph traversal. We are searching
// point of the graph traversal. We are searching backwards to get the // backwards to get the fees first time right and correctly match
// fees first time right and correctly match channel bandwidth. // channel bandwidth.
distance[target] = nodeWithDist{ distance[target] = nodeWithDist{
dist: 0, dist: 0,
weight: 0, weight: 0,
@ -551,7 +531,8 @@ func findPath(g *graphParams, r *RestrictParams, cfg *PathFindingConfig,
// route, return. It is important to also return if the distance // route, return. It is important to also return if the distance
// is equal, because otherwise the algorithm could run into an // is equal, because otherwise the algorithm could run into an
// endless loop. // endless loop.
if tempDist >= distance[fromVertex].dist { current, ok := distance[fromVertex]
if ok && tempDist >= current.dist {
return return
} }