Skip to content

Commit df71a2c

Browse files
committed
slog: move Value access benchmark to new file.
The extensive benchmark was cluttering the file. Pure code motion. Change-Id: I445195e49a199e4930524b98eb9047e046ea87c3 Reviewed-on: https://go-review.googlesource.com/c/exp/+/442359 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Jonathan Amsterdam <jba@google.com> Reviewed-by: Alan Donovan <adonovan@google.com>
1 parent c96df5b commit df71a2c

File tree

2 files changed

+215
-205
lines changed

2 files changed

+215
-205
lines changed
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
// Copyright 2022 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Benchmark for accessing Value values.
6+
7+
package slog
8+
9+
import (
10+
"testing"
11+
"time"
12+
)
13+
14+
// The "As" form is the slowest.
15+
// The switch-panic and visitor times are almost the same.
16+
// BenchmarkDispatch/switch-checked-8 8669427 137.7 ns/op
17+
// BenchmarkDispatch/As-8 8212087 145.3 ns/op
18+
// BenchmarkDispatch/Visit-8 8926146 135.3 ns/op
19+
func BenchmarkDispatch(b *testing.B) {
20+
vs := []Value{
21+
Int64Value(32768),
22+
Uint64Value(0xfacecafe),
23+
StringValue("anything"),
24+
BoolValue(true),
25+
Float64Value(1.2345),
26+
DurationValue(time.Second),
27+
AnyValue(b),
28+
}
29+
var (
30+
ii int64
31+
s string
32+
bb bool
33+
u uint64
34+
d time.Duration
35+
f float64
36+
a any
37+
)
38+
b.Run("switch-checked", func(b *testing.B) {
39+
for i := 0; i < b.N; i++ {
40+
for _, v := range vs {
41+
switch v.Kind() {
42+
case StringKind:
43+
s = v.String()
44+
case Int64Kind:
45+
ii = v.Int64()
46+
case Uint64Kind:
47+
u = v.Uint64()
48+
case Float64Kind:
49+
f = v.Float64()
50+
case BoolKind:
51+
bb = v.Bool()
52+
case DurationKind:
53+
d = v.Duration()
54+
case AnyKind:
55+
a = v.Any()
56+
default:
57+
panic("bad kind")
58+
}
59+
}
60+
}
61+
_ = ii
62+
_ = s
63+
_ = bb
64+
_ = u
65+
_ = d
66+
_ = f
67+
_ = a
68+
69+
})
70+
b.Run("As", func(b *testing.B) {
71+
for i := 0; i < b.N; i++ {
72+
for _, kv := range vs {
73+
if v, ok := kv.AsString(); ok {
74+
s = v
75+
} else if v, ok := kv.AsInt64(); ok {
76+
ii = v
77+
} else if v, ok := kv.AsUint64(); ok {
78+
u = v
79+
} else if v, ok := kv.AsFloat64(); ok {
80+
f = v
81+
} else if v, ok := kv.AsBool(); ok {
82+
bb = v
83+
} else if v, ok := kv.AsDuration(); ok {
84+
d = v
85+
} else if v, ok := kv.AsAny(); ok {
86+
a = v
87+
} else {
88+
panic("bad kind")
89+
}
90+
}
91+
}
92+
_ = ii
93+
_ = s
94+
_ = bb
95+
_ = u
96+
_ = d
97+
_ = f
98+
_ = a
99+
})
100+
101+
b.Run("Visit", func(b *testing.B) {
102+
v := &setVisitor{}
103+
b.ResetTimer()
104+
for i := 0; i < b.N; i++ {
105+
for _, kv := range vs {
106+
kv.Visit(v)
107+
}
108+
}
109+
})
110+
}
111+
112+
type setVisitor struct {
113+
i int64
114+
s string
115+
b bool
116+
u uint64
117+
d time.Duration
118+
f float64
119+
a any
120+
}
121+
122+
func (v *setVisitor) String(s string) { v.s = s }
123+
func (v *setVisitor) Int64(i int64) { v.i = i }
124+
func (v *setVisitor) Uint64(x uint64) { v.u = x }
125+
func (v *setVisitor) Float64(x float64) { v.f = x }
126+
func (v *setVisitor) Bool(x bool) { v.b = x }
127+
func (v *setVisitor) Duration(x time.Duration) { v.d = x }
128+
func (v *setVisitor) Any(x any) { v.a = x }
129+
130+
// When dispatching on all types, the "As" functions are slightly slower
131+
// than switching on the kind and then calling a function that checks
132+
// the kind again. See BenchmarkDispatch above.
133+
134+
func (a Value) AsString() (string, bool) {
135+
if a.Kind() == StringKind {
136+
return a.str(), true
137+
}
138+
return "", false
139+
}
140+
141+
func (a Value) AsInt64() (int64, bool) {
142+
if a.Kind() == Int64Kind {
143+
return int64(a.num), true
144+
}
145+
return 0, false
146+
}
147+
148+
func (a Value) AsUint64() (uint64, bool) {
149+
if a.Kind() == Uint64Kind {
150+
return a.num, true
151+
}
152+
return 0, false
153+
}
154+
155+
func (a Value) AsFloat64() (float64, bool) {
156+
if a.Kind() == Float64Kind {
157+
return a.float(), true
158+
}
159+
return 0, false
160+
}
161+
162+
func (a Value) AsBool() (bool, bool) {
163+
if a.Kind() == BoolKind {
164+
return a.bool(), true
165+
}
166+
return false, false
167+
}
168+
169+
func (a Value) AsDuration() (time.Duration, bool) {
170+
if a.Kind() == DurationKind {
171+
return a.duration(), true
172+
}
173+
return 0, false
174+
}
175+
176+
func (a Value) AsAny() (any, bool) {
177+
if a.Kind() == AnyKind {
178+
return a.any, true
179+
}
180+
return nil, false
181+
}
182+
183+
// Problem: adding a type means adding a method, which is a breaking change.
184+
// Using an unexported method to force embedding will make programs compile,
185+
// But they will panic at runtime when we call the new method.
186+
type Visitor interface {
187+
String(string)
188+
Int64(int64)
189+
Uint64(uint64)
190+
Float64(float64)
191+
Bool(bool)
192+
Duration(time.Duration)
193+
Any(any)
194+
}
195+
196+
func (a Value) Visit(v Visitor) {
197+
switch a.Kind() {
198+
case StringKind:
199+
v.String(a.str())
200+
case Int64Kind:
201+
v.Int64(int64(a.num))
202+
case Uint64Kind:
203+
v.Uint64(a.num)
204+
case BoolKind:
205+
v.Bool(a.bool())
206+
case Float64Kind:
207+
v.Float64(a.float())
208+
case DurationKind:
209+
v.Duration(a.duration())
210+
case AnyKind:
211+
v.Any(a.any)
212+
default:
213+
panic("bad kind")
214+
}
215+
}

0 commit comments

Comments
 (0)