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

View as plain text