utils: cut ParallelExec per-call allocations#1664
Merged
Merged
Conversation
|
ParallelExec is on hot fan-out paths (e.g. the SFU downtrack broadcast, one call per forwarded packet). The parallel branch allocated ~16 objects / ~1.1KB per call: a heap atomic counter, an escaping WaitGroup, and one funcval per spawned goroutine (each carrying the generic type dictionary). Hoist all shared state into a single struct and spawn a single reused worker funcval (a method value bound once), so a parallel call allocates just the struct plus that funcval. The goroutine work-stealing model, step semantics, and behavior are unchanged. Benchmark (NumCPU=14, ~0.2us/item, 3 runs; before -> after): single/f50 8.54us -> 8.41us 1144B/16 -> 104B/2 allocs single/f200 23.16us -> 23.49us 1144B/16 -> 104B/2 allocs concurrent/f50 3.81us -> 3.63us 1144B/16 -> 104B/2 allocs concurrent/f200 14.56us -> 14.43us 1144B/16 -> 104B/2 allocs Latency is unchanged within noise; allocations drop ~87% and bytes ~91%, reducing GC pressure proportionally to the fan-out call rate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ddd8fbf to
9fb81ef
Compare
paulwe
approved these changes
Jul 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Human note:
The biggest impact for this would be live stream kind of cases where the fan out exceeds the threshold. Should be a good win for those cases in terms on reducing GC pressure.
Summary
ParallelExecruns on hot fan-out paths — most notably the SFU downtrack broadcast, which calls it once per forwarded packet. Its parallel branch allocated ~16 objects / ~1.1 KB per call:atomic.NewUint64(0)— a heap-allocated counter,sync.WaitGroup,go func(){…}()on a generic function also carries the type dictionary).At broadcast rates this is a steady source of garbage and GC pressure.
Change
Hoist all shared state (
vals,fn,step,end, the atomic counter, the WaitGroup) into a singleparallelStatestruct, and spawn a single reused worker funcval (a method value bound once) instead of a fresh closure per goroutine. A parallel call now allocates just that struct plus the one worker funcval.The goroutine work-stealing model,
stepsemantics, and observable behavior are unchanged —fnis still invoked exactly once per element, and the serial path (belowparallelThreshold) is untouched.Benchmark
BenchmarkParallelExec, NumCPU=14, ~0.2 µs/item, 3 runs each (before → after):Latency is unchanged within noise (marginally faster in 3 of 4 cases); allocations drop ~87% and allocated bytes ~91%, cutting GC pressure proportionally to the fan-out call rate.
Tests
TestParallel(element coverage) still passes.TestParallelExecConcurrent: 8 concurrent callers × 300 iterations, verifying every element is visited exactly once — passes under-race.🤖 Generated with Claude Code