Skip to content

Commit 88f4458

Browse files
committed
trace: optimize id parsing and string functions
With specialized routines, we can avoid the allocation of hex.DecodeString since we know the structure of the IDs. We can use `==` instead of bytes.Equal for arrays. From the Go [spec]: > Array types are comparable if their array element types are comparable. Two > array values are equal if their corresponding element values are equal. The > elements are compared in ascending index order, and comparison stops as soon > as two element values differ (or all elements have been compared). [spec]: https://go.dev/ref/spec#Comparison_operators ### Benchstat To generate: ```sh mkdir private cd sdk go test -run=xxxxMatchNothingxxxx -bench=. -count=10 go.opentelemetry.io/otel/sdk/trace -timeout=30m | tee ../private/base.txt go test -run=xxxxMatchNothingxxxx -bench=. -count=10 go.opentelemetry.io/otel/sdk/trace -timeout=30m | tee ../private/new.txt benchstat ../private/base.txt ../private/new.txt ``` Results: ``` goos: darwin goarch: arm64 pkg: go.opentelemetry.io/otel/sdk/trace cpu: Apple M2 Max │ ../private/base.txt │ ../private/new.txt │ │ sec/op │ sec/op vs base │ Truncate/Unlimited-12 0.2086n ± 1% 0.2140n ± 3% +2.59% (p=0.017 n=10) Truncate/Zero-12 0.3048n ± 0% 0.3070n ± 1% +0.71% (p=0.014 n=10) Truncate/Short-12 0.2083n ± 2% 0.2097n ± 1% ~ (p=0.148 n=10) Truncate/ASCII-12 0.6870n ± 0% 0.6855n ± 0% ~ (p=0.493 n=10) Truncate/ValidUTF-8-12 1.298n ± 0% 1.302n ± 1% +0.31% (p=0.003 n=10) Truncate/InvalidUTF-8-12 9.457n ± 0% 9.420n ± 1% ~ (p=0.529 n=10) Truncate/MixedUTF-8-12 17.30n ± 1% 17.29n ± 0% ~ (p=0.359 n=10) RecordingSpanSetAttributes/WithLimit/false-12 2.055µ ± 1% 2.082µ ± 9% +1.29% (p=0.014 n=10) RecordingSpanSetAttributes/WithLimit/true-12 4.368µ ± 0% 4.364µ ± 0% -0.08% (p=0.049 n=10) SpanEnd-12 72.57n ± 17% 73.75n ± 16% ~ (p=0.853 n=10) TraceStart/with_a_simple_span-12 320.1n ± 10% 314.6n ± 10% ~ (p=0.165 n=10) TraceStart/with_several_links-12 432.7n ± 2% 429.4n ± 1% ~ (p=0.063 n=10) TraceStart/with_attributes-12 477.3n ± 1% 468.1n ± 6% -1.94% (p=0.005 n=10) SpanLimits/AttributeValueLengthLimit-12 4.401µ ± 1% 4.439µ ± 2% ~ (p=0.089 n=10) SpanLimits/AttributeCountLimit-12 4.125µ ± 1% 4.151µ ± 1% +0.62% (p=0.014 n=10) SpanLimits/EventCountLimit-12 3.900µ ± 2% 3.935µ ± 1% +0.88% (p=0.023 n=10) SpanLimits/LinkCountLimit-12 3.870µ ± 2% 3.901µ ± 1% ~ (p=0.148 n=10) SpanLimits/AttributePerEventCountLimit-12 4.212µ ± 1% 4.243µ ± 1% +0.75% (p=0.008 n=10) SpanLimits/AttributePerLinkCountLimit-12 4.200µ ± 1% 4.224µ ± 0% +0.57% (p=0.041 n=10) SpanSetAttributesOverCapacity-12 1.661µ ± 1% 1.653µ ± 0% -0.48% (p=0.049 n=10) StartEndSpan/AlwaysSample-12 317.9n ± 0% 316.5n ± 0% -0.44% (p=0.007 n=10) StartEndSpan/NeverSample-12 152.3n ± 0% 152.0n ± 0% -0.23% (p=0.005 n=10) SpanWithAttributes_4/AlwaysSample-12 527.2n ± 0% 532.4n ± 1% +1.00% (p=0.000 n=10) SpanWithAttributes_4/NeverSample-12 240.6n ± 0% 241.6n ± 0% +0.46% (p=0.000 n=10) SpanWithAttributes_8/AlwaysSample-12 704.5n ± 0% 718.3n ± 1% +1.97% (p=0.000 n=10) SpanWithAttributes_8/NeverSample-12 325.0n ± 0% 327.2n ± 1% +0.68% (p=0.000 n=10) SpanWithAttributes_all/AlwaysSample-12 576.8n ± 0% 584.7n ± 1% +1.37% (p=0.000 n=10) SpanWithAttributes_all/NeverSample-12 264.6n ± 1% 263.3n ± 0% -0.47% (p=0.045 n=10) SpanWithAttributes_all_2x/AlwaysSample-12 818.6n ± 1% 834.9n ± 0% +1.98% (p=0.000 n=10) SpanWithAttributes_all_2x/NeverSample-12 378.3n ± 0% 382.9n ± 1% +1.23% (p=0.000 n=10) SpanWithEvents_4/AlwaysSample-12 715.1n ± 1% 721.1n ± 0% +0.83% (p=0.003 n=10) SpanWithEvents_4/NeverSample-12 156.1n ± 1% 155.1n ± 1% -0.64% (p=0.002 n=10) SpanWithEvents_8/AlwaysSample-12 1.098µ ± 0% 1.104µ ± 0% +0.55% (p=0.000 n=10) SpanWithEvents_8/NeverSample-12 158.8n ± 0% 158.6n ± 1% ~ (p=0.288 n=10) SpanWithEvents_WithStackTrace/AlwaysSample-12 438.8n ± 0% 438.5n ± 0% ~ (p=0.868 n=10) SpanWithEvents_WithStackTrace/NeverSample-12 168.2n ± 1% 167.4n ± 1% -0.48% (p=0.014 n=10) SpanWithEvents_WithTimestamp/AlwaysSample-12 430.6n ± 0% 432.9n ± 0% +0.53% (p=0.001 n=10) SpanWithEvents_WithTimestamp/NeverSample-12 193.7n ± 0% 190.1n ± 1% -1.91% (p=0.000 n=10) TraceID_DotString-12 42.37n ± 0% 24.80n ± 0% -41.45% (p=0.000 n=10) SpanID_DotString-12 31.30n ± 0% 17.22n ± 0% -44.98% (p=0.000 n=10) SpanProcessorOnEnd/batch:_10,_spans:_10-12 163.3n ± 0% 163.4n ± 0% ~ (p=0.120 n=10) SpanProcessorOnEnd/batch:_10,_spans:_100-12 1.639µ ± 0% 1.635µ ± 0% -0.27% (p=0.000 n=10) SpanProcessorOnEnd/batch:_100,_spans:_10-12 163.3n ± 0% 163.2n ± 0% ~ (p=0.115 n=10) SpanProcessorOnEnd/batch:_100,_spans:_100-12 1.636µ ± 0% 1.635µ ± 1% ~ (p=0.509 n=10) SpanProcessorVerboseLogging-12 6.769µ ± 2% 6.600µ ± 2% -2.49% (p=0.030 n=10) geomean 221.4n 216.4n -2.29% │ ../private/base.txt │ ../private/new.txt │ │ B/op │ B/op vs base │ Truncate/Unlimited-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/Zero-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/Short-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/ASCII-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/ValidUTF-8-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/InvalidUTF-8-12 16.00 ± 0% 16.00 ± 0% ~ (p=1.000 n=10) ¹ Truncate/MixedUTF-8-12 32.00 ± 0% 32.00 ± 0% ~ (p=1.000 n=10) ¹ RecordingSpanSetAttributes/WithLimit/false-12 6.891Ki ± 0% 6.891Ki ± 0% ~ (p=1.000 n=10) ¹ RecordingSpanSetAttributes/WithLimit/true-12 7.023Ki ± 0% 7.023Ki ± 0% ~ (p=1.000 n=10) ¹ SpanEnd-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_a_simple_span-12 528.0 ± 0% 528.0 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_several_links-12 704.0 ± 0% 704.0 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_attributes-12 784.0 ± 0% 784.0 ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/AttributeValueLengthLimit-12 10.56Ki ± 0% 10.56Ki ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/AttributeCountLimit-12 9.844Ki ± 0% 9.844Ki ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/EventCountLimit-12 9.422Ki ± 0% 9.422Ki ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/LinkCountLimit-12 9.031Ki ± 0% 9.031Ki ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/AttributePerEventCountLimit-12 10.47Ki ± 0% 10.47Ki ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/AttributePerLinkCountLimit-12 10.47Ki ± 0% 10.47Ki ± 0% ~ (p=1.000 n=10) ¹ SpanSetAttributesOverCapacity-12 592.0 ± 0% 592.0 ± 0% ~ (p=1.000 n=10) ¹ StartEndSpan/AlwaysSample-12 528.0 ± 0% 528.0 ± 0% ~ (p=1.000 n=10) ¹ StartEndSpan/NeverSample-12 144.0 ± 0% 144.0 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_4/AlwaysSample-12 1.016Ki ± 0% 1.016Ki ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_4/NeverSample-12 400.0 ± 0% 400.0 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_8/AlwaysSample-12 1.516Ki ± 0% 1.516Ki ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_8/NeverSample-12 656.0 ± 0% 656.0 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_all/AlwaysSample-12 1.141Ki ± 0% 1.141Ki ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_all/NeverSample-12 464.0 ± 0% 464.0 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_all_2x/AlwaysSample-12 1.891Ki ± 0% 1.891Ki ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_all_2x/NeverSample-12 848.0 ± 0% 848.0 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_4/AlwaysSample-12 1.016Ki ± 0% 1.016Ki ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_4/NeverSample-12 144.0 ± 0% 144.0 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_8/AlwaysSample-12 1.641Ki ± 0% 1.641Ki ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_8/NeverSample-12 144.0 ± 0% 144.0 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_WithStackTrace/AlwaysSample-12 624.0 ± 0% 624.0 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_WithStackTrace/NeverSample-12 160.0 ± 0% 160.0 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_WithTimestamp/AlwaysSample-12 648.0 ± 0% 648.0 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_WithTimestamp/NeverSample-12 184.0 ± 0% 184.0 ± 0% ~ (p=1.000 n=10) ¹ SpanProcessorOnEnd/batch:_10,_spans:_10-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ SpanProcessorOnEnd/batch:_10,_spans:_100-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ SpanProcessorOnEnd/batch:_100,_spans:_10-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ SpanProcessorOnEnd/batch:_100,_spans:_100-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ SpanProcessorVerboseLogging-12 9.547Ki ± 0% 9.547Ki ± 0% ~ (p=1.000 n=10) ¹ geomean ² +0.00% ² ¹ all samples are equal ² summaries must be >0 to compute geomean │ ../private/base.txt │ ../private/new.txt │ │ allocs/op │ allocs/op vs base │ Truncate/Unlimited-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/Zero-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/Short-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/ASCII-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/ValidUTF-8-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/InvalidUTF-8-12 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹ Truncate/MixedUTF-8-12 1.000 ± 0% 1.000 ± 0% ~ (p=1.000 n=10) ¹ RecordingSpanSetAttributes/WithLimit/false-12 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ RecordingSpanSetAttributes/WithLimit/true-12 10.00 ± 0% 10.00 ± 0% ~ (p=1.000 n=10) ¹ SpanEnd-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_a_simple_span-12 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_several_links-12 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ TraceStart/with_attributes-12 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/AttributeValueLengthLimit-12 41.00 ± 0% 41.00 ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/AttributeCountLimit-12 38.00 ± 0% 38.00 ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/EventCountLimit-12 35.00 ± 0% 35.00 ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/LinkCountLimit-12 35.00 ± 0% 35.00 ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/AttributePerEventCountLimit-12 38.00 ± 0% 38.00 ± 0% ~ (p=1.000 n=10) ¹ SpanLimits/AttributePerLinkCountLimit-12 38.00 ± 0% 38.00 ± 0% ~ (p=1.000 n=10) ¹ SpanSetAttributesOverCapacity-12 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ StartEndSpan/AlwaysSample-12 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=10) ¹ StartEndSpan/NeverSample-12 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_4/AlwaysSample-12 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_4/NeverSample-12 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_8/AlwaysSample-12 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_8/NeverSample-12 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_all/AlwaysSample-12 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_all/NeverSample-12 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_all_2x/AlwaysSample-12 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithAttributes_all_2x/NeverSample-12 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_4/AlwaysSample-12 5.000 ± 0% 5.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_4/NeverSample-12 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_8/AlwaysSample-12 6.000 ± 0% 6.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_8/NeverSample-12 2.000 ± 0% 2.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_WithStackTrace/AlwaysSample-12 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_WithStackTrace/NeverSample-12 3.000 ± 0% 3.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_WithTimestamp/AlwaysSample-12 5.000 ± 0% 5.000 ± 0% ~ (p=1.000 n=10) ¹ SpanWithEvents_WithTimestamp/NeverSample-12 4.000 ± 0% 4.000 ± 0% ~ (p=1.000 n=10) ¹ SpanProcessorOnEnd/batch:_10,_spans:_10-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ SpanProcessorOnEnd/batch:_10,_spans:_100-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ SpanProcessorOnEnd/batch:_100,_spans:_10-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ SpanProcessorOnEnd/batch:_100,_spans:_100-12 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=10) ¹ SpanProcessorVerboseLogging-12 35.00 ± 0% 35.00 ± 0% ~ (p=1.000 n=10) ¹ geomean ² +0.00% ² ¹ all samples are equal ² summaries must be >0 to compute geomean ``` Issue: open-telemetry#6721
1 parent 7a18e17 commit 88f4458

File tree

2 files changed

+114
-48
lines changed

2 files changed

+114
-48
lines changed

trace/hex.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package trace
2+
3+
const (
4+
// hexLU is a hex lookup table.
5+
hexLU = "0123456789abcdef"
6+
// hexRev is a reverse hex lookup table for lowercase hex digits.
7+
hexRev = "" +
8+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
9+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
10+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
11+
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\xff\xff\xff\xff\xff\xff" +
12+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
13+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
14+
"\xff\x0a\x0b\x0c\x0d\x0e\x0f\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
15+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
16+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
17+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
18+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
19+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
20+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
21+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
22+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" +
23+
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
24+
)

trace/trace.go

Lines changed: 90 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
package trace // import "go.opentelemetry.io/otel/trace"
55

66
import (
7-
"bytes"
8-
"encoding/hex"
97
"encoding/json"
108
)
119

@@ -41,18 +39,44 @@ var (
4139
// IsValid checks whether the trace TraceID is valid. A valid trace ID does
4240
// not consist of zeros only.
4341
func (t TraceID) IsValid() bool {
44-
return !bytes.Equal(t[:], nilTraceID[:])
42+
return t != nilTraceID
4543
}
4644

4745
// MarshalJSON implements a custom marshal function to encode TraceID
4846
// as a hex string.
4947
func (t TraceID) MarshalJSON() ([]byte, error) {
50-
return json.Marshal(t.String())
48+
b := [32 + 2]byte{0: '"', 33: '"'}
49+
h := t.hexBytes()
50+
copy(b[1:], h[:])
51+
return b[:], nil
5152
}
5253

5354
// String returns the hex string representation form of a TraceID.
5455
func (t TraceID) String() string {
55-
return hex.EncodeToString(t[:])
56+
h := t.hexBytes()
57+
return string(h[:])
58+
}
59+
60+
// hexBytes returns the hex string representation form of a TraceID.
61+
func (t TraceID) hexBytes() [32]byte {
62+
return [32]byte{
63+
hexLU[t[0x0]>>4], hexLU[t[0x0]&0xf],
64+
hexLU[t[0x1]>>4], hexLU[t[0x1]&0xf],
65+
hexLU[t[0x2]>>4], hexLU[t[0x2]&0xf],
66+
hexLU[t[0x3]>>4], hexLU[t[0x3]&0xf],
67+
hexLU[t[0x4]>>4], hexLU[t[0x4]&0xf],
68+
hexLU[t[0x5]>>4], hexLU[t[0x5]&0xf],
69+
hexLU[t[0x6]>>4], hexLU[t[0x6]&0xf],
70+
hexLU[t[0x7]>>4], hexLU[t[0x7]&0xf],
71+
hexLU[t[0x8]>>4], hexLU[t[0x8]&0xf],
72+
hexLU[t[0x9]>>4], hexLU[t[0x9]&0xf],
73+
hexLU[t[0xa]>>4], hexLU[t[0xa]&0xf],
74+
hexLU[t[0xb]>>4], hexLU[t[0xb]&0xf],
75+
hexLU[t[0xc]>>4], hexLU[t[0xc]&0xf],
76+
hexLU[t[0xd]>>4], hexLU[t[0xd]&0xf],
77+
hexLU[t[0xe]>>4], hexLU[t[0xe]&0xf],
78+
hexLU[t[0xf]>>4], hexLU[t[0xf]&0xf],
79+
}
5680
}
5781

5882
// SpanID is a unique identity of a span in a trace.
@@ -66,78 +90,88 @@ var (
6690
// IsValid checks whether the SpanID is valid. A valid SpanID does not consist
6791
// of zeros only.
6892
func (s SpanID) IsValid() bool {
69-
return !bytes.Equal(s[:], nilSpanID[:])
93+
return s != nilSpanID
7094
}
7195

7296
// MarshalJSON implements a custom marshal function to encode SpanID
7397
// as a hex string.
7498
func (s SpanID) MarshalJSON() ([]byte, error) {
75-
return json.Marshal(s.String())
99+
b := [16 + 2]byte{0: '"', 17: '"'}
100+
h := s.hexBytes()
101+
copy(b[1:], h[:])
102+
return b[:], nil
76103
}
77104

78105
// String returns the hex string representation form of a SpanID.
79106
func (s SpanID) String() string {
80-
return hex.EncodeToString(s[:])
107+
b := s.hexBytes()
108+
return string(b[:])
109+
}
110+
111+
func (s SpanID) hexBytes() [16]byte {
112+
return [16]byte{
113+
hexLU[s[0]>>4], hexLU[s[0]&0xf],
114+
hexLU[s[1]>>4], hexLU[s[1]&0xf],
115+
hexLU[s[2]>>4], hexLU[s[2]&0xf],
116+
hexLU[s[3]>>4], hexLU[s[3]&0xf],
117+
hexLU[s[4]>>4], hexLU[s[4]&0xf],
118+
hexLU[s[5]>>4], hexLU[s[5]&0xf],
119+
hexLU[s[6]>>4], hexLU[s[6]&0xf],
120+
hexLU[s[7]>>4], hexLU[s[7]&0xf],
121+
}
81122
}
82123

83124
// TraceIDFromHex returns a TraceID from a hex string if it is compliant with
84125
// the W3C trace-context specification. See more at
85126
// https://www.w3.org/TR/trace-context/#trace-id
86127
// nolint:revive // revive complains about stutter of `trace.TraceIDFromHex`.
87128
func TraceIDFromHex(h string) (TraceID, error) {
88-
t := TraceID{}
89129
if len(h) != 32 {
90-
return t, errInvalidTraceIDLength
130+
return [16]byte{}, errInvalidTraceIDLength
91131
}
92-
93-
if err := decodeHex(h, t[:]); err != nil {
94-
return t, err
132+
var b [16]byte
133+
invalidMark := byte(0)
134+
for i := 0; i < len(h); i += 4 {
135+
b[i/2] = (hexRev[h[i]] << 4) | hexRev[h[i+1]]
136+
b[i/2+1] = (hexRev[h[i+2]] << 4) | hexRev[h[i+3]]
137+
invalidMark |= hexRev[h[i]] | hexRev[h[i+1]] | hexRev[h[i+2]] | hexRev[h[i+3]]
95138
}
96-
97-
if !t.IsValid() {
98-
return t, errNilTraceID
139+
// If the upper 4 bits of any byte are not zero, there was an invalid hex
140+
// character since invalid hex characters are 0xff in hexLU.
141+
if invalidMark&0xf0 != 0 {
142+
return [16]byte{}, errInvalidHexID
143+
}
144+
// If we didn't set any bits, then h was all zeros.
145+
if invalidMark == 0 {
146+
return [16]byte{}, errNilTraceID
99147
}
100-
return t, nil
148+
return b, nil
101149
}
102150

103151
// SpanIDFromHex returns a SpanID from a hex string if it is compliant
104152
// with the w3c trace-context specification.
105153
// See more at https://www.w3.org/TR/trace-context/#parent-id
106154
func SpanIDFromHex(h string) (SpanID, error) {
107-
s := SpanID{}
108155
if len(h) != 16 {
109-
return s, errInvalidSpanIDLength
110-
}
111-
112-
if err := decodeHex(h, s[:]); err != nil {
113-
return s, err
156+
return [8]byte{}, errInvalidSpanIDLength
114157
}
115-
116-
if !s.IsValid() {
117-
return s, errNilSpanID
158+
var b [8]byte
159+
invalidMark := byte(0)
160+
for i := 0; i < len(h); i += 4 {
161+
b[i/2] = (hexRev[h[i]] << 4) | hexRev[h[i+1]]
162+
b[i/2+1] = (hexRev[h[i+2]] << 4) | hexRev[h[i+3]]
163+
invalidMark |= hexRev[h[i]] | hexRev[h[i+1]] | hexRev[h[i+2]] | hexRev[h[i+3]]
118164
}
119-
return s, nil
120-
}
121-
122-
func decodeHex(h string, b []byte) error {
123-
for _, r := range h {
124-
switch {
125-
case 'a' <= r && r <= 'f':
126-
continue
127-
case '0' <= r && r <= '9':
128-
continue
129-
default:
130-
return errInvalidHexID
131-
}
165+
// If the upper 4 bits of any byte are not zero, there was an invalid hex
166+
// character since invalid hex characters are 0xff in hexLU.
167+
if invalidMark&0xf0 != 0 {
168+
return [8]byte{}, errInvalidHexID
132169
}
133-
134-
decoded, err := hex.DecodeString(h)
135-
if err != nil {
136-
return err
170+
// If we didn't set any bits, then h was all zeros.
171+
if invalidMark == 0 {
172+
return [8]byte{}, errNilSpanID
137173
}
138-
139-
copy(b, decoded)
140-
return nil
174+
return b, nil
141175
}
142176

143177
// TraceFlags contains flags that can be set on a SpanContext.
@@ -160,12 +194,20 @@ func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { // nolint:revive //
160194
// MarshalJSON implements a custom marshal function to encode TraceFlags
161195
// as a hex string.
162196
func (tf TraceFlags) MarshalJSON() ([]byte, error) {
163-
return json.Marshal(tf.String())
197+
b := [2 + 2]byte{0: '"', 3: '"'}
198+
h := tf.hexBytes()
199+
copy(b[1:], h[:])
200+
return b[:], nil
164201
}
165202

166203
// String returns the hex string representation form of TraceFlags.
167204
func (tf TraceFlags) String() string {
168-
return hex.EncodeToString([]byte{byte(tf)}[:])
205+
h := tf.hexBytes()
206+
return string(h[:])
207+
}
208+
209+
func (tf TraceFlags) hexBytes() [2]byte {
210+
return [2]byte{hexLU[tf>>4], hexLU[tf&0xf]}
169211
}
170212

171213
// SpanContextConfig contains mutable fields usable for constructing

0 commit comments

Comments
 (0)