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 i := 0; i < source.NumField(); i++ {
   150  		name := source.Type().Field(i).Name
   151  		value := source.Field(i)
   152  		b.Run(name, func(b *testing.B) {
   153  			for i := 0; i < b.N; i++ {
   154  				sink = value.IsZero()
   155  			}
   156  		})
   157  	}
   158  }
   159  
   160  func BenchmarkSetZero(b *testing.B) {
   161  	source := ValueOf(new(struct {
   162  		Bool      bool
   163  		Int       int64
   164  		Uint      uint64
   165  		Float     float64
   166  		Complex   complex128
   167  		Array     [4]Value
   168  		Chan      chan Value
   169  		Func      func() Value
   170  		Interface interface{ String() string }
   171  		Map       map[string]Value
   172  		Pointer   *Value
   173  		Slice     []Value
   174  		String    string
   175  		Struct    Value
   176  	})).Elem()
   177  
   178  	for i := 0; i < source.NumField(); i++ {
   179  		name := source.Type().Field(i).Name
   180  		value := source.Field(i)
   181  		zero := Zero(value.Type())
   182  		b.Run(name+"/Direct", func(b *testing.B) {
   183  			for i := 0; i < b.N; i++ {
   184  				value.SetZero()
   185  			}
   186  		})
   187  		b.Run(name+"/CachedZero", func(b *testing.B) {
   188  			for i := 0; i < b.N; i++ {
   189  				value.Set(zero)
   190  			}
   191  		})
   192  		b.Run(name+"/NewZero", func(b *testing.B) {
   193  			for i := 0; i < b.N; i++ {
   194  				value.Set(Zero(value.Type()))
   195  			}
   196  		})
   197  	}
   198  }
   199  
   200  // BenchmarkZero overlaps some with BenchmarkSetZero,
   201  // but the inputs are set up differently to exercise
   202  // different optimizations.
   203  func BenchmarkZero(b *testing.B) {
   204  	type bm struct {
   205  		name    string
   206  		zero    Value
   207  		nonZero Value
   208  		size    int
   209  	}
   210  	type Small struct {
   211  		A    int64
   212  		B, C bool
   213  	}
   214  	type Big struct {
   215  		A    int64
   216  		B, C bool
   217  		D    [1008]byte
   218  	}
   219  	entry := func(name string, zero any, nonZero any) bm {
   220  		return bm{name, ValueOf(zero), ValueOf(nonZero).Elem(), int(TypeOf(zero).Size())}
   221  	}
   222  	nonZeroTime := func() *time.Time { t := time.Now(); return &t }
   223  
   224  	bms := []bm{
   225  		entry("ByteArray", [16]byte{}, &[16]byte{1}),
   226  		entry("ByteArray", [64]byte{}, &[64]byte{1}),
   227  		entry("ByteArray", [1024]byte{}, &[1024]byte{1}),
   228  		entry("BigStruct", Big{}, &Big{A: 1}),
   229  		entry("SmallStruct", Small{}, &Small{A: 1}),
   230  		entry("SmallStructArray", [4]Small{}, &[4]Small{0: {A: 1}}),
   231  		entry("SmallStructArray", [64]Small{}, &[64]Small{0: {A: 1}}),
   232  		entry("Time", time.Time{}, nonZeroTime()),
   233  	}
   234  
   235  	for _, bm := range bms {
   236  		b.Run(fmt.Sprintf("IsZero/%s/size=%d", bm.name, bm.size), func(b *testing.B) {
   237  			for i := 0; i < b.N; i++ {
   238  				bm.zero.IsZero()
   239  			}
   240  		})
   241  	}
   242  	for _, bm := range bms {
   243  		b.Run(fmt.Sprintf("SetZero/%s/size=%d", bm.name, bm.size), func(b *testing.B) {
   244  			for i := 0; i < b.N; i++ {
   245  				bm.nonZero.Set(bm.zero)
   246  			}
   247  		})
   248  	}
   249  }
   250  
   251  func BenchmarkSelect(b *testing.B) {
   252  	channel := make(chan int)
   253  	close(channel)
   254  	var cases []SelectCase
   255  	for i := 0; i < 8; i++ {
   256  		cases = append(cases, SelectCase{
   257  			Dir:  SelectRecv,
   258  			Chan: ValueOf(channel),
   259  		})
   260  	}
   261  	for _, numCases := range []int{1, 4, 8} {
   262  		b.Run(strconv.Itoa(numCases), func(b *testing.B) {
   263  			b.ReportAllocs()
   264  			for i := 0; i < b.N; i++ {
   265  				_, _, _ = Select(cases[:numCases])
   266  			}
   267  		})
   268  	}
   269  }
   270  
   271  func BenchmarkCall(b *testing.B) {
   272  	fv := ValueOf(func(a, b string) {})
   273  	b.ReportAllocs()
   274  	b.RunParallel(func(pb *testing.PB) {
   275  		args := []Value{ValueOf("a"), ValueOf("b")}
   276  		for pb.Next() {
   277  			fv.Call(args)
   278  		}
   279  	})
   280  }
   281  
   282  type myint int64
   283  
   284  func (i *myint) inc() {
   285  	*i = *i + 1
   286  }
   287  
   288  func BenchmarkCallMethod(b *testing.B) {
   289  	b.ReportAllocs()
   290  	z := new(myint)
   291  
   292  	v := ValueOf(z.inc)
   293  	for i := 0; i < b.N; i++ {
   294  		v.Call(nil)
   295  	}
   296  }
   297  
   298  func BenchmarkCallArgCopy(b *testing.B) {
   299  	byteArray := func(n int) Value {
   300  		return Zero(ArrayOf(n, TypeOf(byte(0))))
   301  	}
   302  	sizes := [...]struct {
   303  		fv  Value
   304  		arg Value
   305  	}{
   306  		{ValueOf(func(a [128]byte) {}), byteArray(128)},
   307  		{ValueOf(func(a [256]byte) {}), byteArray(256)},
   308  		{ValueOf(func(a [1024]byte) {}), byteArray(1024)},
   309  		{ValueOf(func(a [4096]byte) {}), byteArray(4096)},
   310  		{ValueOf(func(a [65536]byte) {}), byteArray(65536)},
   311  	}
   312  	for _, size := range sizes {
   313  		bench := func(b *testing.B) {
   314  			args := []Value{size.arg}
   315  			b.SetBytes(int64(size.arg.Len()))
   316  			b.ResetTimer()
   317  			b.RunParallel(func(pb *testing.PB) {
   318  				for pb.Next() {
   319  					size.fv.Call(args)
   320  				}
   321  			})
   322  		}
   323  		name := fmt.Sprintf("size=%v", size.arg.Len())
   324  		b.Run(name, bench)
   325  	}
   326  }
   327  
   328  func BenchmarkPtrTo(b *testing.B) {
   329  	// Construct a type with a zero ptrToThis.
   330  	type T struct{ int }
   331  	t := SliceOf(TypeOf(T{}))
   332  	ptrToThis := ValueOf(t).Elem().FieldByName("PtrToThis")
   333  	if !ptrToThis.IsValid() {
   334  		b.Skipf("%v has no ptrToThis field; was it removed from rtype?", t) // TODO fix this at top of refactoring
   335  		// b.Fatalf("%v has no ptrToThis field; was it removed from rtype?", t)
   336  	}
   337  	if ptrToThis.Int() != 0 {
   338  		b.Fatalf("%v.ptrToThis unexpectedly nonzero", t)
   339  	}
   340  	b.ResetTimer()
   341  
   342  	// Now benchmark calling PointerTo on it: we'll have to hit the ptrMap cache on
   343  	// every call.
   344  	b.RunParallel(func(pb *testing.PB) {
   345  		for pb.Next() {
   346  			PointerTo(t)
   347  		}
   348  	})
   349  }
   350  
   351  type B1 struct {
   352  	X int
   353  	Y int
   354  	Z int
   355  }
   356  
   357  func BenchmarkFieldByName1(b *testing.B) {
   358  	t := TypeOf(B1{})
   359  	b.RunParallel(func(pb *testing.PB) {
   360  		for pb.Next() {
   361  			t.FieldByName("Z")
   362  		}
   363  	})
   364  }
   365  
   366  func BenchmarkFieldByName2(b *testing.B) {
   367  	t := TypeOf(S3{})
   368  	b.RunParallel(func(pb *testing.PB) {
   369  		for pb.Next() {
   370  			t.FieldByName("B")
   371  		}
   372  	})
   373  }
   374  
   375  func BenchmarkFieldByName3(b *testing.B) {
   376  	t := TypeOf(R0{})
   377  	b.RunParallel(func(pb *testing.PB) {
   378  		for pb.Next() {
   379  			t.FieldByName("X")
   380  		}
   381  	})
   382  }
   383  
   384  type S struct {
   385  	i1 int64
   386  	i2 int64
   387  }
   388  
   389  func BenchmarkInterfaceBig(b *testing.B) {
   390  	v := ValueOf(S{})
   391  	b.RunParallel(func(pb *testing.PB) {
   392  		for pb.Next() {
   393  			v.Interface()
   394  		}
   395  	})
   396  	b.StopTimer()
   397  }
   398  
   399  func BenchmarkInterfaceSmall(b *testing.B) {
   400  	v := ValueOf(int64(0))
   401  	b.RunParallel(func(pb *testing.PB) {
   402  		for pb.Next() {
   403  			v.Interface()
   404  		}
   405  	})
   406  }
   407  
   408  func BenchmarkNew(b *testing.B) {
   409  	v := TypeOf(XM{})
   410  	b.RunParallel(func(pb *testing.PB) {
   411  		for pb.Next() {
   412  			New(v)
   413  		}
   414  	})
   415  }
   416  
   417  func BenchmarkMap(b *testing.B) {
   418  	type V *int
   419  	type S string
   420  	value := ValueOf((V)(nil))
   421  	stringKeys := []string{}
   422  	mapOfStrings := map[string]V{}
   423  	uint64Keys := []uint64{}
   424  	mapOfUint64s := map[uint64]V{}
   425  	userStringKeys := []S{}
   426  	mapOfUserStrings := map[S]V{}
   427  	for i := 0; i < 100; i++ {
   428  		stringKey := fmt.Sprintf("key%d", i)
   429  		stringKeys = append(stringKeys, stringKey)
   430  		mapOfStrings[stringKey] = nil
   431  
   432  		uint64Key := uint64(i)
   433  		uint64Keys = append(uint64Keys, uint64Key)
   434  		mapOfUint64s[uint64Key] = nil
   435  
   436  		userStringKey := S(fmt.Sprintf("key%d", i))
   437  		userStringKeys = append(userStringKeys, userStringKey)
   438  		mapOfUserStrings[userStringKey] = nil
   439  	}
   440  
   441  	tests := []struct {
   442  		label          string
   443  		m, keys, value Value
   444  	}{
   445  		{"StringKeys", ValueOf(mapOfStrings), ValueOf(stringKeys), value},
   446  		{"Uint64Keys", ValueOf(mapOfUint64s), ValueOf(uint64Keys), value},
   447  		{"UserStringKeys", ValueOf(mapOfUserStrings), ValueOf(userStringKeys), value},
   448  	}
   449  
   450  	for _, tt := range tests {
   451  		b.Run(tt.label, func(b *testing.B) {
   452  			b.Run("MapIndex", func(b *testing.B) {
   453  				b.ReportAllocs()
   454  				for i := 0; i < b.N; i++ {
   455  					for j := tt.keys.Len() - 1; j >= 0; j-- {
   456  						tt.m.MapIndex(tt.keys.Index(j))
   457  					}
   458  				}
   459  			})
   460  			b.Run("SetMapIndex", func(b *testing.B) {
   461  				b.ReportAllocs()
   462  				for i := 0; i < b.N; i++ {
   463  					for j := tt.keys.Len() - 1; j >= 0; j-- {
   464  						tt.m.SetMapIndex(tt.keys.Index(j), tt.value)
   465  					}
   466  				}
   467  			})
   468  		})
   469  	}
   470  }
   471  
   472  func BenchmarkMapIterNext(b *testing.B) {
   473  	m := ValueOf(map[string]int{"a": 0, "b": 1, "c": 2, "d": 3})
   474  	it := m.MapRange()
   475  	for i := 0; i < b.N; i++ {
   476  		for it.Next() {
   477  		}
   478  		it.Reset(m)
   479  	}
   480  }
   481  

View as plain text