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>
diff --git a/slog/value_access_benchmark_test.go b/slog/value_access_benchmark_test.go
new file mode 100644
index 0000000..f92d281
--- /dev/null
+++ b/slog/value_access_benchmark_test.go
@@ -0,0 +1,215 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Benchmark for accessing Value values.
+
+package slog
+
+import (
+ "testing"
+ "time"
+)
+
+// The "As" form is the slowest.
+// The switch-panic and visitor times are almost the same.
+// BenchmarkDispatch/switch-checked-8 8669427 137.7 ns/op
+// BenchmarkDispatch/As-8 8212087 145.3 ns/op
+// BenchmarkDispatch/Visit-8 8926146 135.3 ns/op
+func BenchmarkDispatch(b *testing.B) {
+ vs := []Value{
+ Int64Value(32768),
+ Uint64Value(0xfacecafe),
+ StringValue("anything"),
+ BoolValue(true),
+ Float64Value(1.2345),
+ DurationValue(time.Second),
+ AnyValue(b),
+ }
+ var (
+ ii int64
+ s string
+ bb bool
+ u uint64
+ d time.Duration
+ f float64
+ a any
+ )
+ b.Run("switch-checked", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, v := range vs {
+ switch v.Kind() {
+ case StringKind:
+ s = v.String()
+ case Int64Kind:
+ ii = v.Int64()
+ case Uint64Kind:
+ u = v.Uint64()
+ case Float64Kind:
+ f = v.Float64()
+ case BoolKind:
+ bb = v.Bool()
+ case DurationKind:
+ d = v.Duration()
+ case AnyKind:
+ a = v.Any()
+ default:
+ panic("bad kind")
+ }
+ }
+ }
+ _ = ii
+ _ = s
+ _ = bb
+ _ = u
+ _ = d
+ _ = f
+ _ = a
+
+ })
+ b.Run("As", func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, kv := range vs {
+ if v, ok := kv.AsString(); ok {
+ s = v
+ } else if v, ok := kv.AsInt64(); ok {
+ ii = v
+ } else if v, ok := kv.AsUint64(); ok {
+ u = v
+ } else if v, ok := kv.AsFloat64(); ok {
+ f = v
+ } else if v, ok := kv.AsBool(); ok {
+ bb = v
+ } else if v, ok := kv.AsDuration(); ok {
+ d = v
+ } else if v, ok := kv.AsAny(); ok {
+ a = v
+ } else {
+ panic("bad kind")
+ }
+ }
+ }
+ _ = ii
+ _ = s
+ _ = bb
+ _ = u
+ _ = d
+ _ = f
+ _ = a
+ })
+
+ b.Run("Visit", func(b *testing.B) {
+ v := &setVisitor{}
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for _, kv := range vs {
+ kv.Visit(v)
+ }
+ }
+ })
+}
+
+type setVisitor struct {
+ i int64
+ s string
+ b bool
+ u uint64
+ d time.Duration
+ f float64
+ a any
+}
+
+func (v *setVisitor) String(s string) { v.s = s }
+func (v *setVisitor) Int64(i int64) { v.i = i }
+func (v *setVisitor) Uint64(x uint64) { v.u = x }
+func (v *setVisitor) Float64(x float64) { v.f = x }
+func (v *setVisitor) Bool(x bool) { v.b = x }
+func (v *setVisitor) Duration(x time.Duration) { v.d = x }
+func (v *setVisitor) Any(x any) { v.a = x }
+
+// When dispatching on all types, the "As" functions are slightly slower
+// than switching on the kind and then calling a function that checks
+// the kind again. See BenchmarkDispatch above.
+
+func (a Value) AsString() (string, bool) {
+ if a.Kind() == StringKind {
+ return a.str(), true
+ }
+ return "", false
+}
+
+func (a Value) AsInt64() (int64, bool) {
+ if a.Kind() == Int64Kind {
+ return int64(a.num), true
+ }
+ return 0, false
+}
+
+func (a Value) AsUint64() (uint64, bool) {
+ if a.Kind() == Uint64Kind {
+ return a.num, true
+ }
+ return 0, false
+}
+
+func (a Value) AsFloat64() (float64, bool) {
+ if a.Kind() == Float64Kind {
+ return a.float(), true
+ }
+ return 0, false
+}
+
+func (a Value) AsBool() (bool, bool) {
+ if a.Kind() == BoolKind {
+ return a.bool(), true
+ }
+ return false, false
+}
+
+func (a Value) AsDuration() (time.Duration, bool) {
+ if a.Kind() == DurationKind {
+ return a.duration(), true
+ }
+ return 0, false
+}
+
+func (a Value) AsAny() (any, bool) {
+ if a.Kind() == AnyKind {
+ return a.any, true
+ }
+ return nil, false
+}
+
+// Problem: adding a type means adding a method, which is a breaking change.
+// Using an unexported method to force embedding will make programs compile,
+// But they will panic at runtime when we call the new method.
+type Visitor interface {
+ String(string)
+ Int64(int64)
+ Uint64(uint64)
+ Float64(float64)
+ Bool(bool)
+ Duration(time.Duration)
+ Any(any)
+}
+
+func (a Value) Visit(v Visitor) {
+ switch a.Kind() {
+ case StringKind:
+ v.String(a.str())
+ case Int64Kind:
+ v.Int64(int64(a.num))
+ case Uint64Kind:
+ v.Uint64(a.num)
+ case BoolKind:
+ v.Bool(a.bool())
+ case Float64Kind:
+ v.Float64(a.float())
+ case DurationKind:
+ v.Duration(a.duration())
+ case AnyKind:
+ v.Any(a.any)
+ default:
+ panic("bad kind")
+ }
+}
diff --git a/slog/value_test.go b/slog/value_test.go
index 8e27bda..9ae6ea0 100644
--- a/slog/value_test.go
+++ b/slog/value_test.go
@@ -151,211 +151,6 @@
func (r *replace) LogValue() Value { return r.v }
-//////////////// Benchmark for accessing Value values
-
-// The "As" form is the slowest.
-// The switch-panic and visitor times are almost the same.
-// BenchmarkDispatch/switch-checked-8 8669427 137.7 ns/op
-// BenchmarkDispatch/As-8 8212087 145.3 ns/op
-// BenchmarkDispatch/Visit-8 8926146 135.3 ns/op
-func BenchmarkDispatch(b *testing.B) {
- vs := []Value{
- Int64Value(32768),
- Uint64Value(0xfacecafe),
- StringValue("anything"),
- BoolValue(true),
- Float64Value(1.2345),
- DurationValue(time.Second),
- AnyValue(b),
- }
- var (
- ii int64
- s string
- bb bool
- u uint64
- d time.Duration
- f float64
- a any
- )
- b.Run("switch-checked", func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- for _, v := range vs {
- switch v.Kind() {
- case StringKind:
- s = v.String()
- case Int64Kind:
- ii = v.Int64()
- case Uint64Kind:
- u = v.Uint64()
- case Float64Kind:
- f = v.Float64()
- case BoolKind:
- bb = v.Bool()
- case DurationKind:
- d = v.Duration()
- case AnyKind:
- a = v.Any()
- default:
- panic("bad kind")
- }
- }
- }
- _ = ii
- _ = s
- _ = bb
- _ = u
- _ = d
- _ = f
- _ = a
-
- })
- b.Run("As", func(b *testing.B) {
- for i := 0; i < b.N; i++ {
- for _, kv := range vs {
- if v, ok := kv.AsString(); ok {
- s = v
- } else if v, ok := kv.AsInt64(); ok {
- ii = v
- } else if v, ok := kv.AsUint64(); ok {
- u = v
- } else if v, ok := kv.AsFloat64(); ok {
- f = v
- } else if v, ok := kv.AsBool(); ok {
- bb = v
- } else if v, ok := kv.AsDuration(); ok {
- d = v
- } else if v, ok := kv.AsAny(); ok {
- a = v
- } else {
- panic("bad kind")
- }
- }
- }
- _ = ii
- _ = s
- _ = bb
- _ = u
- _ = d
- _ = f
- _ = a
- })
-
- b.Run("Visit", func(b *testing.B) {
- v := &setVisitor{}
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- for _, kv := range vs {
- kv.Visit(v)
- }
- }
- })
-}
-
-type setVisitor struct {
- i int64
- s string
- b bool
- u uint64
- d time.Duration
- f float64
- a any
-}
-
-func (v *setVisitor) String(s string) { v.s = s }
-func (v *setVisitor) Int64(i int64) { v.i = i }
-func (v *setVisitor) Uint64(x uint64) { v.u = x }
-func (v *setVisitor) Float64(x float64) { v.f = x }
-func (v *setVisitor) Bool(x bool) { v.b = x }
-func (v *setVisitor) Duration(x time.Duration) { v.d = x }
-func (v *setVisitor) Any(x any) { v.a = x }
-
-// When dispatching on all types, the "As" functions are slightly slower
-// than switching on the kind and then calling a function that checks
-// the kind again. See BenchmarkDispatch above.
-
-func (a Value) AsString() (string, bool) {
- if a.Kind() == StringKind {
- return a.str(), true
- }
- return "", false
-}
-
-func (a Value) AsInt64() (int64, bool) {
- if a.Kind() == Int64Kind {
- return int64(a.num), true
- }
- return 0, false
-}
-
-func (a Value) AsUint64() (uint64, bool) {
- if a.Kind() == Uint64Kind {
- return a.num, true
- }
- return 0, false
-}
-
-func (a Value) AsFloat64() (float64, bool) {
- if a.Kind() == Float64Kind {
- return a.float(), true
- }
- return 0, false
-}
-
-func (a Value) AsBool() (bool, bool) {
- if a.Kind() == BoolKind {
- return a.bool(), true
- }
- return false, false
-}
-
-func (a Value) AsDuration() (time.Duration, bool) {
- if a.Kind() == DurationKind {
- return a.duration(), true
- }
- return 0, false
-}
-
-func (a Value) AsAny() (any, bool) {
- if a.Kind() == AnyKind {
- return a.any, true
- }
- return nil, false
-}
-
-// Problem: adding a type means adding a method, which is a breaking change.
-// Using an unexported method to force embedding will make programs compile,
-// But they will panic at runtime when we call the new method.
-type Visitor interface {
- String(string)
- Int64(int64)
- Uint64(uint64)
- Float64(float64)
- Bool(bool)
- Duration(time.Duration)
- Any(any)
-}
-
-func (a Value) Visit(v Visitor) {
- switch a.Kind() {
- case StringKind:
- v.String(a.str())
- case Int64Kind:
- v.Int64(int64(a.num))
- case Uint64Kind:
- v.Uint64(a.num)
- case BoolKind:
- v.Bool(a.bool())
- case Float64Kind:
- v.Float64(a.float())
- case DurationKind:
- v.Duration(a.duration())
- case AnyKind:
- v.Any(a.any)
- default:
- panic("bad kind")
- }
-}
-
// A Value with "unsafe" strings is significantly faster:
// safe: 1785 ns/op, 0 allocs
// unsafe: 690 ns/op, 0 allocs