From 18395615f27d20f395846ededb3929c61a03f9ef Mon Sep 17 00:00:00 2001 From: Ian Cottrell Date: Tue, 14 Apr 2020 13:46:09 -0400 Subject: [PATCH] internal/telemetry: pack strings efficiently into tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TagOfString() deconstructs a string into length and data pointer using the unsafe package, and then stores the length into Tag.packed and the pointer into Tag.untyped, which it can do without allocations. Tag.UnpackString can reconstruct a string on access also using the unsafe package to rebuild the string header. This makes tag significantly smaller, which in turn makes things a faster name old time/op new time/op delta /Baseline-8 160ns ± 6% 158ns ± 8% ~ /StdLog-8 7.50µs ± 8% 7.47µs ±20% ~ /LogNoExporter-8 1.13µs ± 7% 1.04µs ± 2% -8.08% /TraceNoExporter-8 3.61µs ±15% 2.96µs ± 5% -18.08% /StatsNoExporter-8 1.65µs ± 7% 1.39µs ± 7% -16.14% /LogNoop-8 4.43µs ±14% 4.05µs ± 7% ~ /TraceNoop-8 10.9µs ± 5% 10.1µs ± 8% ~ /StatsNoop-8 8.08µs ± 3% 7.42µs ±13% ~ /Log-8 16.2µs ±14% 13.4µs ± 3% -17.10% /Trace-8 61.7µs ±22% 53.6µs ± 7% ~ /Stats-8 11.3µs ±10% 9.5µs ± 4% -15.56% name old alloc/op new alloc/op delta /Baseline-8 0.00B 0.00B ~ /StdLog-8 552B ± 0% 552B ± 0% ~ /LogNoExporter-8 0.00B 0.00B ~ /TraceNoExporter-8 3.58kB ± 0% 2.82kB ± 0% -21.43% /StatsNoExporter-8 0.00B 0.00B ~ /LogNoop-8 3.58kB ± 0% 2.82kB ± 0% -21.43% /TraceNoop-8 11.5kB ± 0% 9.2kB ± 0% -20.00% /StatsNoop-8 7.17kB ± 0% 5.63kB ± 0% -21.43% /Log-8 3.58kB ± 0% 2.82kB ± 0% -21.43% /Trace-8 27.9kB ± 0% 23.6kB ± 0% -15.60% /Stats-8 7.17kB ± 0% 5.63kB ± 0% -21.43% name old allocs/op new allocs/op delta /Baseline-8 0.00 0.00 ~ /StdLog-8 30.0 ± 0% 30.0 ± 0% ~ /LogNoExporter-8 0.00 0.00 ~ /TraceNoExporter-8 16.0 ± 0% 16.0 ± 0% ~ /StatsNoExporter-8 0.00 0.00 ~ /LogNoop-8 16.0 ± 0% 16.0 ± 0% ~ /TraceNoop-8 64.0 ± 0% 64.0 ± 0% ~ /StatsNoop-8 32.0 ± 0% 32.0 ± 0% ~ /Log-8 16.0 ± 0% 16.0 ± 0% ~ /Trace-8 384 ± 0% 384 ± 0% ~ /Stats-8 32.0 ± 0% 32.0 ± 0% ~ Change-Id: If5c369f138b60435f1aa74120aa3c1b68baae402 Reviewed-on: https://go-review.googlesource.com/c/tools/+/228234 Run-TryBot: Ian Cottrell TryBot-Result: Gobot Gobot Reviewed-by: Emmanuel Odeke --- internal/telemetry/event/tag.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/internal/telemetry/event/tag.go b/internal/telemetry/event/tag.go index 2d4fa506c3..8476bc9676 100644 --- a/internal/telemetry/event/tag.go +++ b/internal/telemetry/event/tag.go @@ -7,6 +7,8 @@ package event import ( "fmt" "io" + "reflect" + "unsafe" ) // Tag holds a key and value pair. @@ -14,7 +16,6 @@ import ( type Tag struct { key Key packed uint64 - str string untyped interface{} } @@ -82,13 +83,26 @@ func (t Tag) Unpack64() uint64 { return t.packed } // TagOfString creates a new tag from a key and a string. // This method is for implementing new key types, tag creation should // normally be done with the Of method of the key. -func TagOfString(k Key, v string) Tag { return Tag{key: k, str: v} } +func TagOfString(k Key, v string) Tag { + hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) + return Tag{ + key: k, + packed: uint64(hdr.Len), + untyped: unsafe.Pointer(hdr.Data), + } +} // UnpackString assumes the tag was built using TagOfString and returns the // value that was passed to that constructor. // This method is for implementing new key types, for type safety normal // access should be done with the From method of the key. -func (t Tag) UnpackString() string { return t.str } +func (t Tag) UnpackString() string { + var v string + hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) + hdr.Data = uintptr(t.untyped.(unsafe.Pointer)) + hdr.Len = int(t.packed) + return *(*string)(unsafe.Pointer(hdr)) +} // Valid returns true if the Tag is a valid one (it has a key). func (t Tag) Valid() bool { return t.key != nil }