Source file src/log/slog/value_access_benchmark_test.go

     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 KindString:
    43  					s = v.String()
    44  				case KindInt64:
    45  					ii = v.Int64()
    46  				case KindUint64:
    47  					u = v.Uint64()
    48  				case KindFloat64:
    49  					f = v.Float64()
    50  				case KindBool:
    51  					bb = v.Bool()
    52  				case KindDuration:
    53  					d = v.Duration()
    54  				case KindAny:
    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  		for b.Loop() {
   104  			for _, kv := range vs {
   105  				kv.Visit(v)
   106  			}
   107  		}
   108  	})
   109  }
   110  
   111  type setVisitor struct {
   112  	i int64
   113  	s string
   114  	b bool
   115  	u uint64
   116  	d time.Duration
   117  	f float64
   118  	a any
   119  }
   120  
   121  func (v *setVisitor) String(s string)          { v.s = s }
   122  func (v *setVisitor) Int64(i int64)            { v.i = i }
   123  func (v *setVisitor) Uint64(x uint64)          { v.u = x }
   124  func (v *setVisitor) Float64(x float64)        { v.f = x }
   125  func (v *setVisitor) Bool(x bool)              { v.b = x }
   126  func (v *setVisitor) Duration(x time.Duration) { v.d = x }
   127  func (v *setVisitor) Any(x any)                { v.a = x }
   128  
   129  // When dispatching on all types, the "As" functions are slightly slower
   130  // than switching on the kind and then calling a function that checks
   131  // the kind again. See BenchmarkDispatch above.
   132  
   133  func (a Value) AsString() (string, bool) {
   134  	if a.Kind() == KindString {
   135  		return a.str(), true
   136  	}
   137  	return "", false
   138  }
   139  
   140  func (a Value) AsInt64() (int64, bool) {
   141  	if a.Kind() == KindInt64 {
   142  		return int64(a.num), true
   143  	}
   144  	return 0, false
   145  }
   146  
   147  func (a Value) AsUint64() (uint64, bool) {
   148  	if a.Kind() == KindUint64 {
   149  		return a.num, true
   150  	}
   151  	return 0, false
   152  }
   153  
   154  func (a Value) AsFloat64() (float64, bool) {
   155  	if a.Kind() == KindFloat64 {
   156  		return a.float(), true
   157  	}
   158  	return 0, false
   159  }
   160  
   161  func (a Value) AsBool() (bool, bool) {
   162  	if a.Kind() == KindBool {
   163  		return a.bool(), true
   164  	}
   165  	return false, false
   166  }
   167  
   168  func (a Value) AsDuration() (time.Duration, bool) {
   169  	if a.Kind() == KindDuration {
   170  		return a.duration(), true
   171  	}
   172  	return 0, false
   173  }
   174  
   175  func (a Value) AsAny() (any, bool) {
   176  	if a.Kind() == KindAny {
   177  		return a.any, true
   178  	}
   179  	return nil, false
   180  }
   181  
   182  // Problem: adding a type means adding a method, which is a breaking change.
   183  // Using an unexported method to force embedding will make programs compile,
   184  // But they will panic at runtime when we call the new method.
   185  type Visitor interface {
   186  	String(string)
   187  	Int64(int64)
   188  	Uint64(uint64)
   189  	Float64(float64)
   190  	Bool(bool)
   191  	Duration(time.Duration)
   192  	Any(any)
   193  }
   194  
   195  func (a Value) Visit(v Visitor) {
   196  	switch a.Kind() {
   197  	case KindString:
   198  		v.String(a.str())
   199  	case KindInt64:
   200  		v.Int64(int64(a.num))
   201  	case KindUint64:
   202  		v.Uint64(a.num)
   203  	case KindBool:
   204  		v.Bool(a.bool())
   205  	case KindFloat64:
   206  		v.Float64(a.float())
   207  	case KindDuration:
   208  		v.Duration(a.duration())
   209  	case KindAny:
   210  		v.Any(a.any)
   211  	default:
   212  		panic("bad kind")
   213  	}
   214  }
   215  

View as plain text