1
0
mirror of https://github.com/golang/go synced 2024-11-05 15:56:12 -07:00

internal/telemetry: pack strings efficiently into tags

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 <iancottrell@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
This commit is contained in:
Ian Cottrell 2020-04-14 13:46:09 -04:00
parent be55493b88
commit 18395615f2

View File

@ -7,6 +7,8 @@ package event
import ( import (
"fmt" "fmt"
"io" "io"
"reflect"
"unsafe"
) )
// Tag holds a key and value pair. // Tag holds a key and value pair.
@ -14,7 +16,6 @@ import (
type Tag struct { type Tag struct {
key Key key Key
packed uint64 packed uint64
str string
untyped interface{} 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. // TagOfString creates a new tag from a key and a string.
// This method is for implementing new key types, tag creation should // This method is for implementing new key types, tag creation should
// normally be done with the Of method of the key. // 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 // UnpackString assumes the tag was built using TagOfString and returns the
// value that was passed to that constructor. // value that was passed to that constructor.
// This method is for implementing new key types, for type safety normal // This method is for implementing new key types, for type safety normal
// access should be done with the From method of the key. // 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). // Valid returns true if the Tag is a valid one (it has a key).
func (t Tag) Valid() bool { return t.key != nil } func (t Tag) Valid() bool { return t.key != nil }