perf(propagation): use a hex lookup table to decode traceparent - #8739
Open
itssaharsh wants to merge 6 commits into
Open
perf(propagation): use a hex lookup table to decode traceparent#8739itssaharsh wants to merge 6 commits into
itssaharsh wants to merge 6 commits into
Conversation
extractPart called encoding/hex behind an upperHex guard, and upperHex ranged over the string decoding UTF-8 just to reject the upper-case A-F the spec disallows. A reverse lookup table does both jobs at once: invalid characters map to 0xff, so OR-ing every looked-up value and checking the top 4 bits catches anything invalid, upper-case included. That drops upperHex and the []byte conversion along with it. Extract runs once per incoming request: Extract/Sampled-16 904.3n ± 2% 826.0n ± 3% -8.66% Extract/FutureAdditionalData-16 887.5n ± 1% 788.6n ± 1% -11.14% p=0.000, n=12. Allocations are unchanged; the []byte conversion was already stack allocated. The table goes in internal/shared so there is a single source of truth if other packages need it later.
itssaharsh
requested review from
MrAlias,
XSAM,
dashpole,
dmathieu,
flc1125 and
pellared
as code owners
August 12, 2026 18:16
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8739 +/- ##
=======================================
- Coverage 88.5% 88.5% -0.1%
=======================================
Files 333 333
Lines 21101 21100 -1
=======================================
- Hits 18687 18683 -4
- Misses 2414 2417 +3
🚀 New features to boost your workflow:
|
MrAlias
reviewed
Aug 13, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
Optimizes W3C traceparent extraction in go.opentelemetry.io/otel/propagation by replacing encoding/hex decoding plus a separate uppercase-validation scan with a single-pass reverse-lookup table that both decodes and rejects spec-disallowed uppercase hex.
Changes:
- Rework
traceparentpart decoding to use a reverse hex lookup table with a single validity check. - Add a generated
internal/hextabletable (viagotmpl) and a shared template for reuse. - Add a changelog entry describing the performance/behavior-preserving decoding change.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| propagation/trace_context.go | Switches extractPart to table-based hex decode + validation in one pass. |
| propagation/internal/hextable/hextable.go | Adds generated 256-byte reverse lookup table for lowercase-hex decode/validation. |
| propagation/internal/gen.go | Adds go:generate directive to generate hextable.go from the shared template. |
| internal/shared/hextable/hextable.go.tmpl | Introduces reusable template for generating the reverse lookup table. |
| CHANGELOG.md | Documents the traceparent decoding optimization in the Unreleased section. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
…kup-table # Conflicts: # CHANGELOG.md
Contributor
Author
|
Is there anything I should add here :) @MrAlias |
MrAlias
reviewed
Aug 24, 2026
The template only ever had one consumer. trace/hex.go still has its own private copy, so the generation machinery added moving parts without removing the duplication it was meant to prevent. If we ever do consolidate with trace/hex.go, we can revisit this then.
MrAlias
approved these changes
Aug 24, 2026
dashpole
approved these changes
Aug 27, 2026
Contributor
Author
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.
perf(propagation): use a hex lookup table to decode traceparent
Summary
Replaces
encoding/hexand the manualupperHexvalidation in thetraceparentdecode path with a single-pass reverse lookup table. (encoding/hexis still used byInjectfor encoding.)The "Why"
Currently,
extractPartdoes a UTF-8 range loop over the string just to reject uppercase characters (to satisfy the W3C spec), and then runsencoding/hex.Decode.By using a 256-byte reverse lookup table, we can handle both decoding and validation in one go. Invalid and uppercase characters map to
0xff. Since no valid nibble sets its top 4 bits, OR-ing the looked-up values and checking those bits catches invalid input in one check at the end.Net effect: one less function, no separate validation scan, and no
[]byteconversion.Impact
BogusVersionis unchanged as expected, since it bails on the version field before decoding anything substantial.I ran this three times and got -8.66%, -13.96% and -15.65% on
Extract/Sampled, with the baseline itself moving between 884n and 1181n. That's WSL2 on a laptop, so I'd read it as roughly 9-15% rather than a firm number. Direction was consistent and every run was p<0.001.Allocations: unchanged. I'd assumed the
[]byte(part)cast was allocating and that removing it would show up here. It wasn't, escape analysis already kept it on the stack, so the win is entirely from dropping the range loop and the generic decode.Implementation Notes
gotmplintopropagation/internal/hextable/, matching the convention insdk/log/internal/gen.go.trace/hex.gohas an identical table but it's unexported andtraceis a separate module the root module depends on, sopropagationcan't import it.gotmplfeels like overkill for a single consumer, say so.propagationis in the root module, so a plaininternal/hextablepackage would work with no codegen at all. Easy swap.trace/hex.goonto the same template in a follow-up so there's genuinely one copy. Left it out to keep this focused.0xffsentinel and why it works are documented on the table.trace_context_test.goalready has uppercase rejection tests for all four fields (version, trace ID, span ID, flags), which is exactly whatupperHexprovided, and those pass unchanged.part[i],part[i+1],dst[i/2]). UnlikeTraceIDFromHexthese can't be removed with constant indices, sincenvaries across the fields (2, 32, 16).Testing
go test -race ./...in the root module andsdk, 33 packages, all passingmake generate toolchain-check license-check misspell go-mod-tidy golangci-lint-fix verify-readmes verify-modsclean, no files modified.make generatereproduces the checked-inhextable.gobyte for bytemake precommitdoesn't go green on my machine, but both failures reproduce on an unmodified tree:otlpmetricgrpc'sTestSelfObservabilitygetsDeadlineExceededinstead ofUnavailablebecause WSL2 doesn't refuse a dial to127.0.0.1:1fast enough, andotlploghttpneeds ~66s under-raceagainst the 60s default