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 BenchmarkSelectStaticLit(b *testing.B) {
   269  	channel := make(chan int)
   270  	close(channel)
   271  
   272  	sc := SelectCase{Dir: SelectRecv, Chan: ValueOf(channel)}
   273  	b.Run("[4]SelectCase", func(b *testing.B) {
   274  		for range b.N {
   275  			_, _, _ = Select([]SelectCase{sc, sc, sc, sc})
   276  		}
   277  	})
   278  
   279  	b.Run("[8]SelectCase", func(b *testing.B) {
   280  		for range b.N {
   281  			_, _, _ = Select([]SelectCase{sc, sc, sc, sc, sc, sc, sc, sc})
   282  		}
   283  	})
   284  }
   285  
   286  func BenchmarkCall(b *testing.B) {
   287  	fv := ValueOf(func(a, b string) {})
   288  	b.ReportAllocs()
   289  	b.RunParallel(func(pb *testing.PB) {
   290  		args := []Value{ValueOf("a"), ValueOf("b")}
   291  		for pb.Next() {
   292  			fv.Call(args)
   293  		}
   294  	})
   295  }
   296  
   297  type myint int64
   298  
   299  func (i *myint) inc() {
   300  	*i = *i + 1
   301  }
   302  
   303  func BenchmarkCallMethod(b *testing.B) {
   304  	b.ReportAllocs()
   305  	z := new(myint)
   306  
   307  	v := ValueOf(z.inc)
   308  	for i := 0; i < b.N; i++ {
   309  		v.Call(nil)
   310  	}
   311  }
   312  
   313  func BenchmarkCallArgCopy(b *testing.B) {
   314  	byteArray := func(n int) Value {
   315  		return Zero(ArrayOf(n, TypeOf(byte(0))))
   316  	}
   317  	sizes := [...]struct {
   318  		fv  Value
   319  		arg Value
   320  	}{
   321  		{ValueOf(func(a [128]byte) {}), byteArray(128)},
   322  		{ValueOf(func(a [256]byte) {}), byteArray(256)},
   323  		{ValueOf(func(a [1024]byte) {}), byteArray(1024)},
   324  		{ValueOf(func(a [4096]byte) {}), byteArray(4096)},
   325  		{ValueOf(func(a [65536]byte) {}), byteArray(65536)},
   326  	}
   327  	for _, size := range sizes {
   328  		bench := func(b *testing.B) {
   329  			args := []Value{size.arg}
   330  			b.SetBytes(int64(size.arg.Len()))
   331  			b.ResetTimer()
   332  			b.RunParallel(func(pb *testing.PB) {
   333  				for pb.Next() {
   334  					size.fv.Call(args)
   335  				}
   336  			})
   337  		}
   338  		name := fmt.Sprintf("size=%v", size.arg.Len())
   339  		b.Run(name, bench)
   340  	}
   341  }
   342  
   343  func BenchmarkPtrTo(b *testing.B) {
   344  	// Construct a type with a zero ptrToThis.
   345  	type T struct{ int }
   346  	t := SliceOf(TypeOf(T{}))
   347  	ptrToThis := ValueOf(t).Elem().FieldByName("PtrToThis")
   348  	if !ptrToThis.IsValid() {
   349  		b.Skipf("%v has no ptrToThis field; was it removed from rtype?", t) // TODO fix this at top of refactoring
   350  		// b.Fatalf("%v has no ptrToThis field; was it removed from rtype?", t)
   351  	}
   352  	if ptrToThis.Int() != 0 {
   353  		b.Fatalf("%v.ptrToThis unexpectedly nonzero", t)
   354  	}
   355  	b.ResetTimer()
   356  
   357  	// Now benchmark calling PointerTo on it: we'll have to hit the ptrMap cache on
   358  	// every call.
   359  	b.RunParallel(func(pb *testing.PB) {
   360  		for pb.Next() {
   361  			PointerTo(t)
   362  		}
   363  	})
   364  }
   365  
   366  type B1 struct {
   367  	X int
   368  	Y int
   369  	Z int
   370  }
   371  
   372  func BenchmarkFieldByName1(b *testing.B) {
   373  	t := TypeOf(B1{})
   374  	b.RunParallel(func(pb *testing.PB) {
   375  		for pb.Next() {
   376  			t.FieldByName("Z")
   377  		}
   378  	})
   379  }
   380  
   381  func BenchmarkFieldByName2(b *testing.B) {
   382  	t := TypeOf(S3{})
   383  	b.RunParallel(func(pb *testing.PB) {
   384  		for pb.Next() {
   385  			t.FieldByName("B")
   386  		}
   387  	})
   388  }
   389  
   390  func BenchmarkFieldByName3(b *testing.B) {
   391  	t := TypeOf(R0{})
   392  	b.RunParallel(func(pb *testing.PB) {
   393  		for pb.Next() {
   394  			t.FieldByName("X")
   395  		}
   396  	})
   397  }
   398  
   399  type S struct {
   400  	i1 int64
   401  	i2 int64
   402  }
   403  
   404  func BenchmarkInterfaceBig(b *testing.B) {
   405  	v := ValueOf(S{})
   406  	b.RunParallel(func(pb *testing.PB) {
   407  		for pb.Next() {
   408  			v.Interface()
   409  		}
   410  	})
   411  	b.StopTimer()
   412  }
   413  
   414  func BenchmarkInterfaceSmall(b *testing.B) {
   415  	v := ValueOf(int64(0))
   416  	b.RunParallel(func(pb *testing.PB) {
   417  		for pb.Next() {
   418  			v.Interface()
   419  		}
   420  	})
   421  }
   422  
   423  func BenchmarkNew(b *testing.B) {
   424  	v := TypeOf(XM{})
   425  	b.RunParallel(func(pb *testing.PB) {
   426  		for pb.Next() {
   427  			New(v)
   428  		}
   429  	})
   430  }
   431  
   432  func BenchmarkMap(b *testing.B) {
   433  	type V *int
   434  	type S string
   435  	value := ValueOf((V)(nil))
   436  	stringKeys := []string{}
   437  	mapOfStrings := map[string]V{}
   438  	uint64Keys := []uint64{}
   439  	mapOfUint64s := map[uint64]V{}
   440  	userStringKeys := []S{}
   441  	mapOfUserStrings := map[S]V{}
   442  	for i := 0; i < 100; i++ {
   443  		stringKey := fmt.Sprintf("key%d", i)
   444  		stringKeys = append(stringKeys, stringKey)
   445  		mapOfStrings[stringKey] = nil
   446  
   447  		uint64Key := uint64(i)
   448  		uint64Keys = append(uint64Keys, uint64Key)
   449  		mapOfUint64s[uint64Key] = nil
   450  
   451  		userStringKey := S(fmt.Sprintf("key%d", i))
   452  		userStringKeys = append(userStringKeys, userStringKey)
   453  		mapOfUserStrings[userStringKey] = nil
   454  	}
   455  
   456  	tests := []struct {
   457  		label          string
   458  		m, keys, value Value
   459  	}{
   460  		{"StringKeys", ValueOf(mapOfStrings), ValueOf(stringKeys), value},
   461  		{"Uint64Keys", ValueOf(mapOfUint64s), ValueOf(uint64Keys), value},
   462  		{"UserStringKeys", ValueOf(mapOfUserStrings), ValueOf(userStringKeys), value},
   463  	}
   464  
   465  	for _, tt := range tests {
   466  		b.Run(tt.label, func(b *testing.B) {
   467  			b.Run("MapIndex", func(b *testing.B) {
   468  				b.ReportAllocs()
   469  				for i := 0; i < b.N; i++ {
   470  					for j := tt.keys.Len() - 1; j >= 0; j-- {
   471  						tt.m.MapIndex(tt.keys.Index(j))
   472  					}
   473  				}
   474  			})
   475  			b.Run("SetMapIndex", func(b *testing.B) {
   476  				b.ReportAllocs()
   477  				for i := 0; i < b.N; i++ {
   478  					for j := tt.keys.Len() - 1; j >= 0; j-- {
   479  						tt.m.SetMapIndex(tt.keys.Index(j), tt.value)
   480  					}
   481  				}
   482  			})
   483  		})
   484  	}
   485  }
   486  
   487  func BenchmarkMapIterNext(b *testing.B) {
   488  	m := ValueOf(map[string]int{"a": 0, "b": 1, "c": 2, "d": 3})
   489  	it := m.MapRange()
   490  	for i := 0; i < b.N; i++ {
   491  		for it.Next() {
   492  		}
   493  		it.Reset(m)
   494  	}
   495  }
   496  

View as plain text