metadata: replace added [][]string with O(1) delta linked list#9129
Open
notandruu wants to merge 1 commit into
Open
metadata: replace added [][]string with O(1) delta linked list#9129notandruu wants to merge 1 commit into
notandruu wants to merge 1 commit into
Conversation
AppendToOutgoingContext was O(N) per call: it allocated a new [][]string
of length N+1 and copied all N prior slices into it on every append.
A context with N sequential appends therefore cost O(N²) in total
allocation/copy work.
Replace the flat slice with a singly-linked list of deltaKV nodes.
Each AppendToOutgoingContext call allocates exactly one node and one
flattened kv slice — O(1) regardless of chain depth.
FromOutgoingContext and fromOutgoingContextRaw collect the chain
(newest-first) into a temporary slice, then iterate in reverse to
preserve FIFO ordering, keeping the read path O(N) while maintaining
backward-compatible return types for internal callers.
Benchmark results on Apple M3 Max (go1.26.3):
BenchmarkAppendToOutgoingContext (accumulating context, num=10):
before: 3,263,813 ns/op (quadratic growth, chain length ≈ 10·N)
after: 1,646 ns/op (1982× faster)
BenchmarkFromOutgoingContext:
before: 544.3 ns/op
after: 212.4 ns/op (2.6× faster)
BenchmarkAppendToOutgoingContextN (fresh context, N appends):
N= 1: 87 ns/op, 160 B/op, 4 allocs
N= 5: 446 ns/op, 800 B/op, 20 allocs
N=10: 872 ns/op, 1600 B/op, 40 allocs
N=50: 4373 ns/op, 8000 B/op, 200 allocs
(linear: ~87·N ns/op confirmed)
Fixes grpc#8860
Signed-off-by: Andrew Liu <andrewjliu22@gmail.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #9129 +/- ##
==========================================
+ Coverage 83.20% 83.25% +0.04%
==========================================
Files 414 414
Lines 33489 33496 +7
==========================================
+ Hits 27865 27887 +22
+ Misses 4214 4201 -13
+ Partials 1410 1408 -2
🚀 New features to boost your workflow:
|
Author
|
Could a maintainer add a |
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.
Problem
AppendToOutgoingContextis O(N) per call because it allocates a new[][]stringof length N+1 and copies all N prior slices on everyinvocation. A call chain with N sequential appends costs O(N²) in total
allocation and copy work. This is visible in production as latency spikes
in metadata-heavy RPCs.
Fixes #8860.
Solution
Replace
rawMD.added [][]stringwith a singly-linked list ofdeltaKVnodes. Each
AppendToOutgoingContextcall allocates exactly one node andone flattened kv slice — O(1) regardless of chain depth.
FromOutgoingContextandfromOutgoingContextRawtraverse the chain(newest-first), collect into a temporary slice, then iterate in reverse
to preserve FIFO ordering, keeping the read path O(N) and maintaining
backward-compatible return types for internal callers in transport and
stream code.
Benchmarks
Apple M3 Max, go1.26.3:
All existing metadata tests pass.
RELEASE NOTES:
AppendToOutgoingContextis now O(1) per call instead of O(N), eliminating quadratic allocation overhead in metadata-heavy RPC call chains.