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 5package reflect_test 6 7import ( 8 "fmt" 9 . "reflect" 10 "strconv" 11 "testing" 12) 13 14var 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 32var sinkAll struct { 33 RawBool bool 34 RawString string 35 RawBytes []byte 36 RawInt int 37} 38 39func BenchmarkBool(b *testing.B) { 40 for i := 0; i < b.N; i++ { 41 sinkAll.RawBool = sourceAll.Bool.Bool() 42 } 43} 44 45func BenchmarkString(b *testing.B) { 46 for i := 0; i < b.N; i++ { 47 sinkAll.RawString = sourceAll.String.String() 48 } 49} 50 51func BenchmarkBytes(b *testing.B) { 52 for i := 0; i < b.N; i++ { 53 sinkAll.RawBytes = sourceAll.Bytes.Bytes() 54 } 55} 56 57func BenchmarkNamedBytes(b *testing.B) { 58 for i := 0; i < b.N; i++ { 59 sinkAll.RawBytes = sourceAll.NamedBytes.Bytes() 60 } 61} 62 63func BenchmarkBytesArray(b *testing.B) { 64 for i := 0; i < b.N; i++ { 65 sinkAll.RawBytes = sourceAll.BytesArray.Bytes() 66 } 67} 68 69func BenchmarkSliceLen(b *testing.B) { 70 for i := 0; i < b.N; i++ { 71 sinkAll.RawInt = sourceAll.SliceAny.Len() 72 } 73} 74 75func BenchmarkMapLen(b *testing.B) { 76 for i := 0; i < b.N; i++ { 77 sinkAll.RawInt = sourceAll.MapStringAny.Len() 78 } 79} 80 81func BenchmarkStringLen(b *testing.B) { 82 for i := 0; i < b.N; i++ { 83 sinkAll.RawInt = sourceAll.String.Len() 84 } 85} 86 87func BenchmarkArrayLen(b *testing.B) { 88 for i := 0; i < b.N; i++ { 89 sinkAll.RawInt = sourceAll.BytesArray.Len() 90 } 91} 92 93func BenchmarkSliceCap(b *testing.B) { 94 for i := 0; i < b.N; i++ { 95 sinkAll.RawInt = sourceAll.SliceAny.Cap() 96 } 97} 98 99func 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 110func 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 122func 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 159func 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 199func 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 219func 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 230type myint int64 231 232func (i *myint) inc() { 233 *i = *i + 1 234} 235 236func 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 246func 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 276func 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 299type B1 struct { 300 X int 301 Y int 302 Z int 303} 304 305func 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 314func 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 323func 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 332type S struct { 333 i1 int64 334 i2 int64 335} 336 337func 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 347func 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 356func 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 365func 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 420func 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