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:
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 {
|
lnwire.TLVOnionPayloadOptional,
|
||||||
return nil
|
)
|
||||||
|
if !supportsTLV {
|
||||||
|
return nil, fmt.Errorf("destination hop doesn't " +
|
||||||
|
"understand new TLV paylods")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
|
||||||
)
|
|
||||||
if !supportsTLV {
|
|
||||||
return fmt.Errorf("destination hop doesn't " +
|
|
||||||
"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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user