Source file src/reflect/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  package reflect_test
     6  
     7  import (
     8  	"fmt"
     9  	. "reflect"
    10  	"strconv"
    11  	"testing"
    12  )
    13  
    14  var sourceAll = struct {
    15  	Bool         Value
    16  	String       Value
    17  	Bytes        Value
    18  	NamedBytes   Value
    19  	BytesArray   Value
    20  	SliceAny     Value
    21  	MapStringAny Value
    22  }{
    23  	Bool:         ValueOf(new(bool)).Elem(),
    24  	String:       ValueOf(new(string)).Elem(),
    25  	Bytes:        ValueOf(new([]byte)).Elem(),
    26  	NamedBytes:   ValueOf(new(namedBytes)).Elem(),
    27  	BytesArray:   ValueOf(new([32]byte)).Elem(),
    28  	SliceAny:     ValueOf(new([]any)).Elem(),
    29  	MapStringAny: ValueOf(new(map[string]any)).Elem(),
    30  }
    31  
    32  var sinkAll struct {
    33  	RawBool   bool
    34  	RawString string
    35  	RawBytes  []byte
    36  	RawInt    int
    37  }
    38  
    39  func BenchmarkBool(b *testing.B) {
    40  	for i := 0; i < b.N; i++ {
    41  		sinkAll.RawBool = sourceAll.Bool.Bool()
    42  	}
    43  }
    44  
    45  func BenchmarkString(b *testing.B) {
    46  	for i := 0; i < b.N; i++ {
    47  		sinkAll.RawString = sourceAll.String.String()
    48  	}
    49  }
    50  
    51  func BenchmarkBytes(b *testing.B) {
    52  	for i := 0; i < b.N; i++ {
    53  		sinkAll.RawBytes = sourceAll.Bytes.Bytes()
    54  	}
    55  }
    56  
    57  func BenchmarkNamedBytes(b *testing.B) {
    58  	for i := 0; i < b.N; i++ {
    59  		sinkAll.RawBytes = sourceAll.NamedBytes.Bytes()
    60  	}
    61  }
    62  
    63  func BenchmarkBytesArray(b *testing.B) {
    64  	for i := 0; i < b.N; i++ {
    65  		sinkAll.RawBytes = sourceAll.BytesArray.Bytes()
    66  	}
    67  }
    68  
    69  func BenchmarkSliceLen(b *testing.B) {
    70  	for i := 0; i < b.N; i++ {
    71  		sinkAll.RawInt = sourceAll.SliceAny.Len()
    72  	}
    73  }
    74  
    75  func BenchmarkMapLen(b *testing.B) {
    76  	for i := 0; i < b.N; i++ {
    77  		sinkAll.RawInt = sourceAll.MapStringAny.Len()
    78  	}
    79  }
    80  
    81  func BenchmarkStringLen(b *testing.B) {
    82  	for i := 0; i < b.N; i++ {
    83  		sinkAll.RawInt = sourceAll.String.Len()
    84  	}
    85  }
    86  
    87  func BenchmarkArrayLen(b *testing.B) {
    88  	for i := 0; i < b.N; i++ {
    89  		sinkAll.RawInt = sourceAll.BytesArray.Len()
    90  	}
    91  }
    92  
    93  func BenchmarkSliceCap(b *testing.B) {
    94  	for i := 0; i < b.N; i++ {
    95  		sinkAll.RawInt = sourceAll.SliceAny.Cap()
    96  	}
    97  }
    98  
    99  func BenchmarkDeepEqual(b *testing.B) {
   100  	for _, bb := range deepEqualPerfTests {
   101  		b.Run(ValueOf(bb.x).Type().String(), func(b *testing.B) {
   102  			b.ReportAllocs()
   103  			for i := 0; i < b.N; i++ {
   104  				sink = DeepEqual(bb.x, bb.y)
   105  			}
   106  		})
   107  	}
   108  }
   109  
   110  func BenchmarkMapsDeepEqual(b *testing.B) {
   111  	m1 := map[int]int{
   112  		1: 1, 2: 2,
   113  	}
   114  	m2 := map[int]int{
   115  		1: 1, 2: 2,
   116  	}
   117  	for i := 0; i < b.N; i++ {
   118  		DeepEqual(m1, m2)
   119  	}
   120  }
   121  
   122  func BenchmarkIsZero(b *testing.B) {
   123  	type Int4 struct {
   124  		a, b, c, d int
   125  	}
   126  	type Int1024 struct {
   127  		a [1024]int
   128  	}
   129  	type Int512 struct {
   130  		a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16 [16]S
   131  	}
   132  	s := struct {
   133  		ArrayComparable      [4]T
   134  		ArrayIncomparable    [4]_Complex
   135  		StructComparable     T
   136  		StructIncomparable   _Complex
   137  		ArrayInt_4           [4]int
   138  		ArrayInt_1024        [1024]int
   139  		ArrayInt_1024_NoZero [1024]int
   140  		Struct4Int           Int4
   141  		ArrayStruct4Int_1024 [256]Int4
   142  		ArrayChanInt_1024    [1024]chan int
   143  		StructInt_512        Int512
   144  	}{}
   145  	s.ArrayInt_1024_NoZero[512] = 1
   146  	source := ValueOf(s)
   147  
   148  	for i := 0; i < source.NumField(); i++ {
   149  		name := source.Type().Field(i).Name
   150  		value := source.Field(i)
   151  		b.Run(name, func(b *testing.B) {
   152  			for i := 0; i < b.N; i++ {
   153  				sink = value.IsZero()
   154  			}
   155  		})
   156  	}
   157  }
   158  
   159  func BenchmarkSetZero(b *testing.B) {
   160  	source := ValueOf(new(struct {
   161  		Bool      bool
   162  		Int       int64
   163  		Uint      uint64
   164  		Float     float64
   165  		Complex   complex128
   166  		Array     [4]Value
   167  		Chan      chan Value
   168  		Func      func() Value
   169  		Interface interface{ String() string }
   170  		Map       map[string]Value
   171  		Pointer   *Value
   172  		Slice     []Value
   173  		String    string
   174  		Struct    Value
   175  	})).Elem()
   176  
   177  	for i := 0; i < source.NumField(); i++ {
   178  		name := source.Type().Field(i).Name
   179  		value := source.Field(i)
   180  		zero := Zero(value.Type())
   181  		b.Run(name+"/Direct", func(b *testing.B) {
   182  			for i := 0; i < b.N; i++ {
   183  				value.SetZero()
   184  			}
   185  		})
   186  		b.Run(name+"/CachedZero", func(b *testing.B) {
   187  			for i := 0; i < b.N; i++ {
   188  				value.Set(zero)
   189  			}
   190  		})
   191  		b.Run(name+"/NewZero", func(b *testing.B) {
   192  			for i := 0; i < b.N; i++ {
   193  				value.Set(Zero(value.Type()))
   194  			}
   195  		})
   196  	}
   197  }
   198  
   199  func BenchmarkSelect(b *testing.B) {
   200  	channel := make(chan int)
   201  	close(channel)
   202  	var cases []SelectCase
   203  	for i := 0; i < 8; i++ {
   204  		cases = append(cases, SelectCase{
   205  			Dir:  SelectRecv,
   206  			Chan: ValueOf(channel),
   207  		})
   208  	}
   209  	for _, numCases := range []int{1, 4, 8} {
   210  		b.Run(strconv.Itoa(numCases), func(b *testing.B) {
   211  			b.ReportAllocs()
   212  			for i := 0; i < b.N; i++ {
   213  				_, _, _ = Select(cases[:numCases])
   214  			}
   215  		})
   216  	}
   217  }
   218  
   219  func BenchmarkCall(b *testing.B) {
   220  	fv := ValueOf(func(a, b string) {})
   221  	b.ReportAllocs()
   222  	b.RunParallel(func(pb *testing.PB) {
   223  		args := []Value{ValueOf("a"), ValueOf("b")}
   224  		for pb.Next() {
   225  			fv.Call(args)
   226  		}
   227  	})
   228  }
   229  
   230  type myint int64
   231  
   232  func (i *myint) inc() {
   233  	*i = *i + 1
   234  }
   235  
   236  func BenchmarkCallMethod(b *testing.B) {
   237  	b.ReportAllocs()
   238  	z := new(myint)
   239  
   240  	v := ValueOf(z.inc)
   241  	for i := 0; i < b.N; i++ {
   242  		v.Call(nil)
   243  	}
   244  }
   245  
   246  func BenchmarkCallArgCopy(b *testing.B) {
   247  	byteArray := func(n int) Value {
   248  		return Zero(ArrayOf(n, TypeOf(byte(0))))
   249  	}
   250  	sizes := [...]struct {
   251  		fv  Value
   252  		arg Value
   253  	}{
   254  		{ValueOf(func(a [128]byte) {}), byteArray(128)},
   255  		{ValueOf(func(a [256]byte) {}), byteArray(256)},
   256  		{ValueOf(func(a [1024]byte) {}), byteArray(1024)},
   257  		{ValueOf(func(a [4096]byte) {}), byteArray(4096)},
   258  		{ValueOf(func(a [65536]byte) {}), byteArray(65536)},
   259  	}
   260  	for _, size := range sizes {
   261  		bench := func(b *testing.B) {
   262  			args := []Value{size.arg}
   263  			b.SetBytes(int64(size.arg.Len()))
   264  			b.ResetTimer()
   265  			b.RunParallel(func(pb *testing.PB) {
   266  				for pb.Next() {
   267  					size.fv.Call(args)
   268  				}
   269  			})
   270  		}
   271  		name := fmt.Sprintf("size=%v", size.arg.Len())
   272  		b.Run(name, bench)
   273  	}
   274  }
   275  
   276  func BenchmarkPtrTo(b *testing.B) {
   277  	// Construct a type with a zero ptrToThis.
   278  	type T struct{ int }
   279  	t := SliceOf(TypeOf(T{}))
   280  	ptrToThis := ValueOf(t).Elem().FieldByName("PtrToThis")
   281  	if !ptrToThis.IsValid() {
   282  		b.Skipf("%v has no ptrToThis field; was it removed from rtype?", t) // TODO fix this at top of refactoring
   283  		// b.Fatalf("%v has no ptrToThis field; was it removed from rtype?", t)
   284  	}
   285  	if ptrToThis.Int() != 0 {
   286  		b.Fatalf("%v.ptrToThis unexpectedly nonzero", t)
   287  	}
   288  	b.ResetTimer()
   289  
   290  	// Now benchmark calling PointerTo on it: we'll have to hit the ptrMap cache on
   291  	// every call.
   292  	b.RunParallel(func(pb *testing.PB) {
   293  		for pb.Next() {
   294  			PointerTo(t)
   295  		}
   296  	})
   297  }
   298  
   299  type B1 struct {
   300  	X int
   301  	Y int
   302  	Z int
   303  }
   304  
   305  func BenchmarkFieldByName1(b *testing.B) {
   306  	t := TypeOf(B1{})
   307  	b.RunParallel(func(pb *testing.PB) {
   308  		for pb.Next() {
   309  			t.FieldByName("Z")
   310  		}
   311  	})
   312  }
   313  
   314  func BenchmarkFieldByName2(b *testing.B) {
   315  	t := TypeOf(S3{})
   316  	b.RunParallel(func(pb *testing.PB) {
   317  		for pb.Next() {
   318  			t.FieldByName("B")
   319  		}
   320  	})
   321  }
   322  
   323  func BenchmarkFieldByName3(b *testing.B) {
   324  	t := TypeOf(R0{})
   325  	b.RunParallel(func(pb *testing.PB) {
   326  		for pb.Next() {
   327  			t.FieldByName("X")
   328  		}
   329  	})
   330  }
   331  
   332  type S struct {
   333  	i1 int64
   334  	i2 int64
   335  }
   336  
   337  func BenchmarkInterfaceBig(b *testing.B) {
   338  	v := ValueOf(S{})
   339  	b.RunParallel(func(pb *testing.PB) {
   340  		for pb.Next() {
   341  			v.Interface()
   342  		}
   343  	})
   344  	b.StopTimer()
   345  }
   346  
   347  func BenchmarkInterfaceSmall(b *testing.B) {
   348  	v := ValueOf(int64(0))
   349  	b.RunParallel(func(pb *testing.PB) {
   350  		for pb.Next() {
   351  			v.Interface()
   352  		}
   353  	})
   354  }
   355  
   356  func BenchmarkNew(b *testing.B) {
   357  	v := TypeOf(XM{})
   358  	b.RunParallel(func(pb *testing.PB) {
   359  		for pb.Next() {
   360  			New(v)
   361  		}
   362  	})
   363  }
   364  
   365  func BenchmarkMap(b *testing.B) {
   366  	type V *int
   367  	type S string
   368  	value := ValueOf((V)(nil))
   369  	stringKeys := []string{}
   370  	mapOfStrings := map[string]V{}
   371  	uint64Keys := []uint64{}
   372  	mapOfUint64s := map[uint64]V{}
   373  	userStringKeys := []S{}
   374  	mapOfUserStrings := map[S]V{}
   375  	for i := 0; i < 100; i++ {
   376  		stringKey := fmt.Sprintf("key%d", i)
   377  		stringKeys = append(stringKeys, stringKey)
   378  		mapOfStrings[stringKey] = nil
   379  
   380  		uint64Key := uint64(i)
   381  		uint64Keys = append(uint64Keys, uint64Key)
   382  		mapOfUint64s[uint64Key] = nil
   383  
   384  		userStringKey := S(fmt.Sprintf("key%d", i))
   385  		userStringKeys = append(userStringKeys, userStringKey)
   386  		mapOfUserStrings[userStringKey] = nil
   387  	}
   388  
   389  	tests := []struct {
   390  		label          string
   391  		m, keys, value Value
   392  	}{
   393  		{"StringKeys", ValueOf(mapOfStrings), ValueOf(stringKeys), value},
   394  		{"Uint64Keys", ValueOf(mapOfUint64s), ValueOf(uint64Keys), value},
   395  		{"UserStringKeys", ValueOf(mapOfUserStrings), ValueOf(userStringKeys), value},
   396  	}
   397  
   398  	for _, tt := range tests {
   399  		b.Run(tt.label, func(b *testing.B) {
   400  			b.Run("MapIndex", func(b *testing.B) {
   401  				b.ReportAllocs()
   402  				for i := 0; i < b.N; i++ {
   403  					for j := tt.keys.Len() - 1; j >= 0; j-- {
   404  						tt.m.MapIndex(tt.keys.Index(j))
   405  					}
   406  				}
   407  			})
   408  			b.Run("SetMapIndex", func(b *testing.B) {
   409  				b.ReportAllocs()
   410  				for i := 0; i < b.N; i++ {
   411  					for j := tt.keys.Len() - 1; j >= 0; j-- {
   412  						tt.m.SetMapIndex(tt.keys.Index(j), tt.value)
   413  					}
   414  				}
   415  			})
   416  		})
   417  	}
   418  }
   419  
   420  func BenchmarkMapIterNext(b *testing.B) {
   421  	m := ValueOf(map[string]int{"a": 0, "b": 1, "c": 2, "d": 3})
   422  	it := m.MapRange()
   423  	for i := 0; i < b.N; i++ {
   424  		for it.Next() {
   425  		}
   426  		it.Reset(m)
   427  	}
   428  }
   429  

View as plain text