From f449d7a424309b60605515df6a05b66bf86b2425 Mon Sep 17 00:00:00 2001 From: lczyk Date: Fri, 10 Jul 2026 08:58:41 +0100 Subject: [PATCH 1/2] perf: reuse conflict traversal queues The traversal queues regrew from nil on every path insertion, which accounted for ~90% of allocated bytes. BenchmarkConflictTree/pkgs=1000: -91% B/op, -37% allocs/op, -17% sec/op. --- internal/setup/conflict.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/internal/setup/conflict.go b/internal/setup/conflict.go index 4fceaeb15..fd725c6de 100644 --- a/internal/setup/conflict.go +++ b/internal/setup/conflict.go @@ -52,6 +52,10 @@ type node struct { type pathConflictTree struct { Root *node PathToSlices map[string][]*Slice + // currentQueue and nextQueue are kept across pathHasConflict calls so + // that the traversal does not have to grow fresh queues for every path. + currentQueue []*node + nextQueue []*node } var rootSegment = segment{"/", false, false} @@ -100,11 +104,17 @@ func (g *pathConflictTree) pathHasConflict(newSegments []segment, newSegmentSlic return fmt.Errorf("slices %s and %s conflict on %s and %s", oldSlice, newSlice, oldPath, newPath) } - var currentQueue []*node - var nextQueue []*node + currentQueue := g.currentQueue[:0] + nextQueue := g.nextQueue[:0] + defer func() { + // Keep the grown queues for the next call. + g.currentQueue, g.nextQueue = currentQueue, nextQueue + }() // Skip "/". - currentQueue = slices.Collect(maps.Values(g.Root.Children)) + for _, child := range g.Root.Children { + currentQueue = append(currentQueue, child) + } newSegments = newSegments[1:] // If we run out of segments from the graph or the path there cannot be a From 3c097a782def5822011301adf7dc532c046e02b7 Mon Sep 17 00:00:00 2001 From: lczyk Date: Fri, 10 Jul 2026 09:03:14 +0100 Subject: [PATCH 2/2] perf: exact-match child lookup in traversal A literal segment scanned every sibling node linearly even though Children is keyed by segment text. Look up the exact child directly and scan only wildcard children, tracked in a new GlobChildren list. BenchmarkConflictTree/pkgs=1000: -70% sec/op; DeepPaths: -85% sec/op. --- internal/setup/conflict.go | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/internal/setup/conflict.go b/internal/setup/conflict.go index fd725c6de..5310a6879 100644 --- a/internal/setup/conflict.go +++ b/internal/setup/conflict.go @@ -32,6 +32,10 @@ type node struct { Segment segment SegmentSlices []*segmentSlice Children map[string]*node + // GlobChildren lists the children whose segment contains a wildcard, so + // that a literal segment can find its exact match in Children and only + // scan these. + GlobChildren []*node } // pathConflictTree uses a custom trie to find conflicts that might arise from @@ -112,10 +116,11 @@ func (g *pathConflictTree) pathHasConflict(newSegments []segment, newSegmentSlic }() // Skip "/". - for _, child := range g.Root.Children { - currentQueue = append(currentQueue, child) - } newSegments = newSegments[1:] + if len(newSegments) == 0 { + return nil + } + currentQueue = appendCandidates(currentQueue, g.Root, newSegments[0]) // If we run out of segments from the graph or the path there cannot be a // conflict. @@ -162,8 +167,8 @@ func (g *pathConflictTree) pathHasConflict(newSegments []segment, newSegmentSlic // If we are at the terminal node of both paths we found a conflict. return conflictErrMsg(oldSegmentSlice, newSegmentSlice) } - for _, child := range oldNode.Children { - nextQueue = append(nextQueue, child) + if len(newSegments) > 1 { + nextQueue = appendCandidates(nextQueue, oldNode, newSegments[1]) } break oldNodeLoop } else { @@ -184,6 +189,23 @@ func (g *pathConflictTree) pathHasConflict(newSegments []segment, newSegmentSlic return nil } +// appendCandidates appends the children of parent that can possibly match +// seg. A segment with a wildcard can match any child, but a literal segment +// can only match the child with the exact same text or children with +// wildcards. +func appendCandidates(queue []*node, parent *node, seg segment) []*node { + if seg.HasGlob { + for _, child := range parent.Children { + queue = append(queue, child) + } + return queue + } + if child, ok := parent.Children[seg.Text]; ok { + queue = append(queue, child) + } + return append(queue, parent.GlobChildren...) +} + // insertSegments inserts the path's segments blindly in the graph without // looking at conflicts. func (g *pathConflictTree) insertSegments(segments []segment, segmentSlices []*segmentSlice) { @@ -198,9 +220,12 @@ func (g *pathConflictTree) insertSegments(segments []segment, segmentSlices []*s Segment: segment, Children: map[string]*node{}, } + parent.Children[segment.Text] = current + if segment.HasGlob { + parent.GlobChildren = append(parent.GlobChildren, current) + } } current.SegmentSlices = append(current.SegmentSlices, segmentSlices...) - parent.Children[segment.Text] = current parent = current } }