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