From 021049e0891df3b7eee092f2f7f4050713de8ff6 Mon Sep 17 00:00:00 2001 From: Varun Patil Date: Wed, 25 Dec 2024 00:36:37 -0800 Subject: [PATCH] fw: remove heap allocations in incoming interest pipeline --- fw/core/logger.go | 8 ++++++++ fw/fw/bestroute.go | 47 +++++++++++++++++++++++++++++++++++++--------- fw/fw/multicast.go | 23 +++++++++++++++++------ fw/fw/strategy.go | 7 ++++++- fw/fw/thread.go | 35 +++++++++++++++++++++++++--------- 5 files changed, 95 insertions(+), 25 deletions(-) diff --git a/fw/core/logger.go b/fw/core/logger.go index d4475b0f..5f6aadda 100644 --- a/fw/core/logger.go +++ b/fw/core/logger.go @@ -137,3 +137,11 @@ func LogTrace(module interface{}, components ...interface{}) { log.Debug(generateLogMessage(module, components...)) } } + +func HasTraceLogs() bool { + return shouldPrintTraceLogs +} + +func HasDebugLogs() bool { + return logLevel <= log.DebugLevel +} diff --git a/fw/fw/bestroute.go b/fw/fw/bestroute.go index c38c017d..f7df2214 100644 --- a/fw/fw/bestroute.go +++ b/fw/fw/bestroute.go @@ -8,7 +8,6 @@ package fw import ( - "sort" "time" "github.com/named-data/ndnd/fw/core" @@ -63,10 +62,13 @@ func (s *BestRoute) AfterReceiveInterest( packet *defn.Pkt, pitEntry table.PitEntry, inFace uint64, - nexthops []*table.FibNextHopEntry, + nexthops [MaxNextHops]*table.FibNextHopEntry, + nexthopsCount int, ) { - if len(nexthops) == 0 { - core.LogDebug(s, "AfterReceiveInterest: No nexthop for Interest=", packet.Name, " - DROP") + if nexthopsCount == 0 { + if core.HasDebugLogs() { + core.LogDebug(s, "AfterReceiveInterest: No nexthop for Interest=", packet.Name, " - DROP") + } return } @@ -75,21 +77,48 @@ func (s *BestRoute) AfterReceiveInterest( for _, outRecord := range pitEntry.OutRecords() { if outRecord.LatestNonce != *packet.L3.Interest.Nonce() && outRecord.LatestTimestamp.Add(BestRouteSuppressionTime).After(time.Now()) { - core.LogDebug(s, "AfterReceiveInterest: Suppressed Interest=", packet.Name, " - DROP") + if core.HasDebugLogs() { + core.LogDebug(s, "AfterReceiveInterest: Suppressed Interest=", packet.Name, " - DROP") + } return } } // Sort nexthops by cost and send to best-possible nexthop - sort.Slice(nexthops, func(i, j int) bool { return nexthops[i].Cost < nexthops[j].Cost }) - for _, nh := range nexthops { - core.LogTrace(s, "AfterReceiveInterest: Forwarding Interest=", packet.Name, " to FaceID=", nh.Nexthop) + lowesthops := nexthops + getLowest := func() *table.FibNextHopEntry { + cost := uint64(1 << 63) + index := -1 + for i := 0; i < nexthopsCount; i++ { + nh := nexthops[i] + if nh == nil { + continue + } + if nh.Cost < cost { + cost = nh.Cost + index = i + } + } + if index == -1 { + return nil + } + hop := nexthops[index] + lowesthops[index] = nil + return hop + } + + for nh := getLowest(); nh != nil; nh = getLowest() { + if core.HasTraceLogs() { + core.LogTrace(s, "AfterReceiveInterest: Forwarding Interest=", packet.Name, " to FaceID=", nh.Nexthop) + } if sent := s.SendInterest(packet, pitEntry, nh.Nexthop, inFace); sent { return } } - core.LogDebug(s, "AfterReceiveInterest: No usable nexthop for Interest=", packet.Name, " - DROP") + if core.HasDebugLogs() { + core.LogDebug(s, "AfterReceiveInterest: No usable nexthop for Interest=", packet.Name, " - DROP") + } } func (s *BestRoute) BeforeSatisfyInterest(pitEntry table.PitEntry, inFace uint64) { diff --git a/fw/fw/multicast.go b/fw/fw/multicast.go index d5b25118..bf681021 100644 --- a/fw/fw/multicast.go +++ b/fw/fw/multicast.go @@ -61,10 +61,13 @@ func (s *Multicast) AfterReceiveInterest( packet *defn.Pkt, pitEntry table.PitEntry, inFace uint64, - nexthops []*table.FibNextHopEntry, + nexthops [MaxNextHops]*table.FibNextHopEntry, + nexthopsCount int, ) { - if len(nexthops) == 0 { - core.LogDebug(s, "AfterReceiveInterest: No nexthop for Interest=", packet.Name, " - DROP") + if nexthopsCount == 0 { + if core.HasDebugLogs() { + core.LogDebug(s, "AfterReceiveInterest: No nexthop for Interest=", packet.Name, " - DROP") + } return } @@ -73,14 +76,22 @@ func (s *Multicast) AfterReceiveInterest( for _, outRecord := range pitEntry.OutRecords() { if outRecord.LatestNonce != *packet.L3.Interest.Nonce() && outRecord.LatestTimestamp.Add(MulticastSuppressionTime).After(time.Now()) { - core.LogDebug(s, "AfterReceiveInterest: Suppressed Interest=", packet.Name, " - DROP") + if core.HasDebugLogs() { + core.LogDebug(s, "AfterReceiveInterest: Suppressed Interest=", packet.Name, " - DROP") + } return } } // Send interest to all nexthops - for _, nexthop := range nexthops { - core.LogTrace(s, "AfterReceiveInterest: Forwarding Interest=", packet.Name, " to FaceID=", nexthop.Nexthop) + for i := 0; i < nexthopsCount; i++ { + nexthop := nexthops[i] + if nexthop == nil { + continue + } + if core.HasTraceLogs() { + core.LogTrace(s, "AfterReceiveInterest: Forwarding Interest=", packet.Name, " to FaceID=", nexthop.Nexthop) + } s.SendInterest(packet, pitEntry, nexthop.Nexthop, inFace) } } diff --git a/fw/fw/strategy.go b/fw/fw/strategy.go index 9ecdbe50..55ac379b 100644 --- a/fw/fw/strategy.go +++ b/fw/fw/strategy.go @@ -18,6 +18,9 @@ import ( // StrategyPrefix is the prefix of all strategy names for YaNFD const StrategyPrefix = "/localhost/nfd/strategy" +// Maximum number of possible next hops (i.e. router links) for optimization +const MaxNextHops = 64 + // Strategy represents a forwarding strategy. type Strategy interface { Instantiate(fwThread *Thread) @@ -36,7 +39,9 @@ type Strategy interface { packet *defn.Pkt, pitEntry table.PitEntry, inFace uint64, - nexthops []*table.FibNextHopEntry) + nexthops [MaxNextHops]*table.FibNextHopEntry, + nexthopsCount int, + ) BeforeSatisfyInterest( pitEntry table.PitEntry, inFace uint64) diff --git a/fw/fw/thread.go b/fw/fw/thread.go index 14d61444..61bc0135 100644 --- a/fw/fw/thread.go +++ b/fw/fw/thread.go @@ -188,7 +188,9 @@ func (t *Thread) processIncomingInterest(packet *defn.Pkt) { } // Log PIT token (if any) - core.LogTrace(t, "OnIncomingInterest: ", packet.Name, ", FaceID=", incomingFace.FaceID(), ", PitTokenL=", len(packet.PitToken)) + if core.HasTraceLogs() { + core.LogTrace(t, "OnIncomingInterest: ", packet.Name, ", FaceID=", incomingFace.FaceID(), ", PitTokenL=", len(packet.PitToken)) + } // Check if violates /localhost if incomingFace.Scope() == defn.NonLocal && @@ -223,13 +225,17 @@ func (t *Thread) processIncomingInterest(packet *defn.Pkt) { // Drop packet if no nonce is found if interest.Nonce() == nil { - core.LogDebug(t, "Interest ", packet.Name, " is missing Nonce - DROP") + if core.HasDebugLogs() { + core.LogDebug(t, "Interest ", packet.Name, " is missing Nonce - DROP") + } return } // Check if packet is in dead nonce list if exists := t.deadNonceList.Find(interest.Name(), *interest.Nonce()); exists { - core.LogDebug(t, "Interest ", packet.Name, " is dropped by DeadNonce: ", *interest.Nonce()) + if core.HasDebugLogs() { + core.LogDebug(t, "Interest ", packet.Name, " is dropped by DeadNonce: ", *interest.Nonce()) + } return } @@ -238,7 +244,9 @@ func (t *Thread) processIncomingInterest(packet *defn.Pkt) { pitEntry, isDuplicate := t.pitCS.InsertInterest(interest, fhName, incomingFace.FaceID()) if isDuplicate { // Interest loop - since we don't use Nacks, just drop - core.LogDebug(t, "Interest ", packet.Name, " is looping - DROP") + if core.HasDebugLogs() { + core.LogDebug(t, "Interest ", packet.Name, " is looping - DROP") + } return } @@ -278,7 +286,9 @@ func (t *Thread) processIncomingInterest(packet *defn.Pkt) { } } } else { - core.LogTrace(t, "Interest ", packet.Name, " is already pending") + if core.HasTraceLogs() { + core.LogTrace(t, "Interest ", packet.Name, " is already pending") + } // Add the previous nonce to the dead nonce list to prevent further looping // TODO: review this design, not specified in NFD dev guide @@ -291,7 +301,10 @@ func (t *Thread) processIncomingInterest(packet *defn.Pkt) { // If NextHopFaceId set, forward to that face (if it exists) or drop if packet.NextHopFaceID != nil { if dispatch.GetFace(*packet.NextHopFaceID) != nil { - core.LogTrace(t, "NextHopFaceId is set for Interest ", packet.Name, " - dispatching directly to face") + if core.HasTraceLogs() { + core.LogTrace(t, "NextHopFaceId is set for Interest ", packet.Name, " - dispatching directly to face") + } + dispatch.GetFace(*packet.NextHopFaceID).SendPacket(dispatch.OutPkt{ Pkt: packet, PitToken: packet.PitToken, // TODO: ?? @@ -314,16 +327,20 @@ func (t *Thread) processIncomingInterest(packet *defn.Pkt) { // Exclude faces that have an in-record for this interest // TODO: unclear where NFD dev guide specifies such behavior (if any) - allowedNexthops := make([]*table.FibNextHopEntry, 0, len(nexthops)) + allowedNexthops := [MaxNextHops]*table.FibNextHopEntry{} + allowedNexthopsCount := 0 for _, nexthop := range nexthops { record := pitEntry.InRecords()[nexthop.Nexthop] if record == nil || nexthop.Nexthop == incomingFace.FaceID() { - allowedNexthops = append(allowedNexthops, nexthop) + allowedNexthops[allowedNexthopsCount] = nexthop + allowedNexthopsCount++ } } // Pass to strategy AfterReceiveInterest pipeline - strategy.AfterReceiveInterest(packet, pitEntry, incomingFace.FaceID(), allowedNexthops) + strategy.AfterReceiveInterest( + packet, pitEntry, incomingFace.FaceID(), + allowedNexthops, allowedNexthopsCount) } func (t *Thread) processOutgoingInterest(