1// Copyright 2011 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// Large data benchmark.
6// The JSON data is a summary of agl's changes in the
7// go, webkit, and chromium open source projects.
8// We benchmark converting between the JSON form
9// and in-memory data structures.
10
11package json
12
13import (
14	"bytes"
15	"compress/gzip"
16	"fmt"
17	"internal/testenv"
18	"io"
19	"os"
20	"reflect"
21	"regexp"
22	"runtime"
23	"strings"
24	"sync"
25	"testing"
26)
27
28type codeResponse struct {
29	Tree     *codeNode `json:"tree"`
30	Username string    `json:"username"`
31}
32
33type codeNode struct {
34	Name     string      `json:"name"`
35	Kids     []*codeNode `json:"kids"`
36	CLWeight float64     `json:"cl_weight"`
37	Touches  int         `json:"touches"`
38	MinT     int64       `json:"min_t"`
39	MaxT     int64       `json:"max_t"`
40	MeanT    int64       `json:"mean_t"`
41}
42
43var codeJSON []byte
44var codeStruct codeResponse
45
46func codeInit() {
47	f, err := os.Open("testdata/code.json.gz")
48	if err != nil {
49		panic(err)
50	}
51	defer f.Close()
52	gz, err := gzip.NewReader(f)
53	if err != nil {
54		panic(err)
55	}
56	data, err := io.ReadAll(gz)
57	if err != nil {
58		panic(err)
59	}
60
61	codeJSON = data
62
63	if err := Unmarshal(codeJSON, &codeStruct); err != nil {
64		panic("unmarshal code.json: " + err.Error())
65	}
66
67	if data, err = Marshal(&codeStruct); err != nil {
68		panic("marshal code.json: " + err.Error())
69	}
70
71	if !bytes.Equal(data, codeJSON) {
72		println("different lengths", len(data), len(codeJSON))
73		for i := 0; i < len(data) && i < len(codeJSON); i++ {
74			if data[i] != codeJSON[i] {
75				println("re-marshal: changed at byte", i)
76				println("orig: ", string(codeJSON[i-10:i+10]))
77				println("new: ", string(data[i-10:i+10]))
78				break
79			}
80		}
81		panic("re-marshal code.json: different result")
82	}
83}
84
85func BenchmarkCodeEncoder(b *testing.B) {
86	b.ReportAllocs()
87	if codeJSON == nil {
88		b.StopTimer()
89		codeInit()
90		b.StartTimer()
91	}
92	b.RunParallel(func(pb *testing.PB) {
93		enc := NewEncoder(io.Discard)
94		for pb.Next() {
95			if err := enc.Encode(&codeStruct); err != nil {
96				b.Fatalf("Encode error: %v", err)
97			}
98		}
99	})
100	b.SetBytes(int64(len(codeJSON)))
101}
102
103func BenchmarkCodeEncoderError(b *testing.B) {
104	b.ReportAllocs()
105	if codeJSON == nil {
106		b.StopTimer()
107		codeInit()
108		b.StartTimer()
109	}
110
111	// Trigger an error in Marshal with cyclic data.
112	type Dummy struct {
113		Name string
114		Next *Dummy
115	}
116	dummy := Dummy{Name: "Dummy"}
117	dummy.Next = &dummy
118
119	b.RunParallel(func(pb *testing.PB) {
120		enc := NewEncoder(io.Discard)
121		for pb.Next() {
122			if err := enc.Encode(&codeStruct); err != nil {
123				b.Fatalf("Encode error: %v", err)
124			}
125			if _, err := Marshal(dummy); err == nil {
126				b.Fatal("Marshal error: got nil, want non-nil")
127			}
128		}
129	})
130	b.SetBytes(int64(len(codeJSON)))
131}
132
133func BenchmarkCodeMarshal(b *testing.B) {
134	b.ReportAllocs()
135	if codeJSON == nil {
136		b.StopTimer()
137		codeInit()
138		b.StartTimer()
139	}
140	b.RunParallel(func(pb *testing.PB) {
141		for pb.Next() {
142			if _, err := Marshal(&codeStruct); err != nil {
143				b.Fatalf("Marshal error: %v", err)
144			}
145		}
146	})
147	b.SetBytes(int64(len(codeJSON)))
148}
149
150func BenchmarkCodeMarshalError(b *testing.B) {
151	b.ReportAllocs()
152	if codeJSON == nil {
153		b.StopTimer()
154		codeInit()
155		b.StartTimer()
156	}
157
158	// Trigger an error in Marshal with cyclic data.
159	type Dummy struct {
160		Name string
161		Next *Dummy
162	}
163	dummy := Dummy{Name: "Dummy"}
164	dummy.Next = &dummy
165
166	b.RunParallel(func(pb *testing.PB) {
167		for pb.Next() {
168			if _, err := Marshal(&codeStruct); err != nil {
169				b.Fatalf("Marshal error: %v", err)
170			}
171			if _, err := Marshal(dummy); err == nil {
172				b.Fatal("Marshal error: got nil, want non-nil")
173			}
174		}
175	})
176	b.SetBytes(int64(len(codeJSON)))
177}
178
179func benchMarshalBytes(n int) func(*testing.B) {
180	sample := []byte("hello world")
181	// Use a struct pointer, to avoid an allocation when passing it as an
182	// interface parameter to Marshal.
183	v := &struct {
184		Bytes []byte
185	}{
186		bytes.Repeat(sample, (n/len(sample))+1)[:n],
187	}
188	return func(b *testing.B) {
189		for i := 0; i < b.N; i++ {
190			if _, err := Marshal(v); err != nil {
191				b.Fatalf("Marshal error: %v", err)
192			}
193		}
194	}
195}
196
197func benchMarshalBytesError(n int) func(*testing.B) {
198	sample := []byte("hello world")
199	// Use a struct pointer, to avoid an allocation when passing it as an
200	// interface parameter to Marshal.
201	v := &struct {
202		Bytes []byte
203	}{
204		bytes.Repeat(sample, (n/len(sample))+1)[:n],
205	}
206
207	// Trigger an error in Marshal with cyclic data.
208	type Dummy struct {
209		Name string
210		Next *Dummy
211	}
212	dummy := Dummy{Name: "Dummy"}
213	dummy.Next = &dummy
214
215	return func(b *testing.B) {
216		for i := 0; i < b.N; i++ {
217			if _, err := Marshal(v); err != nil {
218				b.Fatalf("Marshal error: %v", err)
219			}
220			if _, err := Marshal(dummy); err == nil {
221				b.Fatal("Marshal error: got nil, want non-nil")
222			}
223		}
224	}
225}
226
227func BenchmarkMarshalBytes(b *testing.B) {
228	b.ReportAllocs()
229	// 32 fits within encodeState.scratch.
230	b.Run("32", benchMarshalBytes(32))
231	// 256 doesn't fit in encodeState.scratch, but is small enough to
232	// allocate and avoid the slower base64.NewEncoder.
233	b.Run("256", benchMarshalBytes(256))
234	// 4096 is large enough that we want to avoid allocating for it.
235	b.Run("4096", benchMarshalBytes(4096))
236}
237
238func BenchmarkMarshalBytesError(b *testing.B) {
239	b.ReportAllocs()
240	// 32 fits within encodeState.scratch.
241	b.Run("32", benchMarshalBytesError(32))
242	// 256 doesn't fit in encodeState.scratch, but is small enough to
243	// allocate and avoid the slower base64.NewEncoder.
244	b.Run("256", benchMarshalBytesError(256))
245	// 4096 is large enough that we want to avoid allocating for it.
246	b.Run("4096", benchMarshalBytesError(4096))
247}
248
249func BenchmarkMarshalMap(b *testing.B) {
250	b.ReportAllocs()
251	m := map[string]int{
252		"key3": 3,
253		"key2": 2,
254		"key1": 1,
255	}
256	b.RunParallel(func(pb *testing.PB) {
257		for pb.Next() {
258			if _, err := Marshal(m); err != nil {
259				b.Fatal("Marshal:", err)
260			}
261		}
262	})
263}
264
265func BenchmarkCodeDecoder(b *testing.B) {
266	b.ReportAllocs()
267	if codeJSON == nil {
268		b.StopTimer()
269		codeInit()
270		b.StartTimer()
271	}
272	b.RunParallel(func(pb *testing.PB) {
273		var buf bytes.Buffer
274		dec := NewDecoder(&buf)
275		var r codeResponse
276		for pb.Next() {
277			buf.Write(codeJSON)
278			// hide EOF
279			buf.WriteByte('\n')
280			buf.WriteByte('\n')
281			buf.WriteByte('\n')
282			if err := dec.Decode(&r); err != nil {
283				b.Fatalf("Decode error: %v", err)
284			}
285		}
286	})
287	b.SetBytes(int64(len(codeJSON)))
288}
289
290func BenchmarkUnicodeDecoder(b *testing.B) {
291	b.ReportAllocs()
292	j := []byte(`"\uD83D\uDE01"`)
293	b.SetBytes(int64(len(j)))
294	r := bytes.NewReader(j)
295	dec := NewDecoder(r)
296	var out string
297	b.ResetTimer()
298	for i := 0; i < b.N; i++ {
299		if err := dec.Decode(&out); err != nil {
300			b.Fatalf("Decode error: %v", err)
301		}
302		r.Seek(0, 0)
303	}
304}
305
306func BenchmarkDecoderStream(b *testing.B) {
307	b.ReportAllocs()
308	b.StopTimer()
309	var buf bytes.Buffer
310	dec := NewDecoder(&buf)
311	buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n")
312	var x any
313	if err := dec.Decode(&x); err != nil {
314		b.Fatalf("Decode error: %v", err)
315	}
316	ones := strings.Repeat(" 1\n", 300000) + "\n\n\n"
317	b.StartTimer()
318	for i := 0; i < b.N; i++ {
319		if i%300000 == 0 {
320			buf.WriteString(ones)
321		}
322		x = nil
323		switch err := dec.Decode(&x); {
324		case err != nil:
325			b.Fatalf("Decode error: %v", err)
326		case x != 1.0:
327			b.Fatalf("Decode: got %v want 1.0", i)
328		}
329	}
330}
331
332func BenchmarkCodeUnmarshal(b *testing.B) {
333	b.ReportAllocs()
334	if codeJSON == nil {
335		b.StopTimer()
336		codeInit()
337		b.StartTimer()
338	}
339	b.RunParallel(func(pb *testing.PB) {
340		for pb.Next() {
341			var r codeResponse
342			if err := Unmarshal(codeJSON, &r); err != nil {
343				b.Fatalf("Unmarshal error: %v", err)
344			}
345		}
346	})
347	b.SetBytes(int64(len(codeJSON)))
348}
349
350func BenchmarkCodeUnmarshalReuse(b *testing.B) {
351	b.ReportAllocs()
352	if codeJSON == nil {
353		b.StopTimer()
354		codeInit()
355		b.StartTimer()
356	}
357	b.RunParallel(func(pb *testing.PB) {
358		var r codeResponse
359		for pb.Next() {
360			if err := Unmarshal(codeJSON, &r); err != nil {
361				b.Fatalf("Unmarshal error: %v", err)
362			}
363		}
364	})
365	b.SetBytes(int64(len(codeJSON)))
366}
367
368func BenchmarkUnmarshalString(b *testing.B) {
369	b.ReportAllocs()
370	data := []byte(`"hello, world"`)
371	b.RunParallel(func(pb *testing.PB) {
372		var s string
373		for pb.Next() {
374			if err := Unmarshal(data, &s); err != nil {
375				b.Fatalf("Unmarshal error: %v", err)
376			}
377		}
378	})
379}
380
381func BenchmarkUnmarshalFloat64(b *testing.B) {
382	b.ReportAllocs()
383	data := []byte(`3.14`)
384	b.RunParallel(func(pb *testing.PB) {
385		var f float64
386		for pb.Next() {
387			if err := Unmarshal(data, &f); err != nil {
388				b.Fatalf("Unmarshal error: %v", err)
389			}
390		}
391	})
392}
393
394func BenchmarkUnmarshalInt64(b *testing.B) {
395	b.ReportAllocs()
396	data := []byte(`3`)
397	b.RunParallel(func(pb *testing.PB) {
398		var x int64
399		for pb.Next() {
400			if err := Unmarshal(data, &x); err != nil {
401				b.Fatalf("Unmarshal error: %v", err)
402			}
403		}
404	})
405}
406
407func BenchmarkUnmarshalMap(b *testing.B) {
408	b.ReportAllocs()
409	data := []byte(`{"key1":"value1","key2":"value2","key3":"value3"}`)
410	b.RunParallel(func(pb *testing.PB) {
411		x := make(map[string]string, 3)
412		for pb.Next() {
413			if err := Unmarshal(data, &x); err != nil {
414				b.Fatalf("Unmarshal error: %v", err)
415			}
416		}
417	})
418}
419
420func BenchmarkIssue10335(b *testing.B) {
421	b.ReportAllocs()
422	j := []byte(`{"a":{ }}`)
423	b.RunParallel(func(pb *testing.PB) {
424		var s struct{}
425		for pb.Next() {
426			if err := Unmarshal(j, &s); err != nil {
427				b.Fatalf("Unmarshal error: %v", err)
428			}
429		}
430	})
431}
432
433func BenchmarkIssue34127(b *testing.B) {
434	b.ReportAllocs()
435	j := struct {
436		Bar string `json:"bar,string"`
437	}{
438		Bar: `foobar`,
439	}
440	b.RunParallel(func(pb *testing.PB) {
441		for pb.Next() {
442			if _, err := Marshal(&j); err != nil {
443				b.Fatalf("Marshal error: %v", err)
444			}
445		}
446	})
447}
448
449func BenchmarkUnmapped(b *testing.B) {
450	b.ReportAllocs()
451	j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`)
452	b.RunParallel(func(pb *testing.PB) {
453		var s struct{}
454		for pb.Next() {
455			if err := Unmarshal(j, &s); err != nil {
456				b.Fatalf("Unmarshal error: %v", err)
457			}
458		}
459	})
460}
461
462func BenchmarkTypeFieldsCache(b *testing.B) {
463	b.ReportAllocs()
464	var maxTypes int = 1e6
465	if testenv.Builder() != "" {
466		maxTypes = 1e3 // restrict cache sizes on builders
467	}
468
469	// Dynamically generate many new types.
470	types := make([]reflect.Type, maxTypes)
471	fs := []reflect.StructField{{
472		Type:  reflect.TypeFor[string](),
473		Index: []int{0},
474	}}
475	for i := range types {
476		fs[0].Name = fmt.Sprintf("TypeFieldsCache%d", i)
477		types[i] = reflect.StructOf(fs)
478	}
479
480	// clearClear clears the cache. Other JSON operations, must not be running.
481	clearCache := func() {
482		fieldCache = sync.Map{}
483	}
484
485	// MissTypes tests the performance of repeated cache misses.
486	// This measures the time to rebuild a cache of size nt.
487	for nt := 1; nt <= maxTypes; nt *= 10 {
488		ts := types[:nt]
489		b.Run(fmt.Sprintf("MissTypes%d", nt), func(b *testing.B) {
490			nc := runtime.GOMAXPROCS(0)
491			for i := 0; i < b.N; i++ {
492				clearCache()
493				var wg sync.WaitGroup
494				for j := 0; j < nc; j++ {
495					wg.Add(1)
496					go func(j int) {
497						for _, t := range ts[(j*len(ts))/nc : ((j+1)*len(ts))/nc] {
498							cachedTypeFields(t)
499						}
500						wg.Done()
501					}(j)
502				}
503				wg.Wait()
504			}
505		})
506	}
507
508	// HitTypes tests the performance of repeated cache hits.
509	// This measures the average time of each cache lookup.
510	for nt := 1; nt <= maxTypes; nt *= 10 {
511		// Pre-warm a cache of size nt.
512		clearCache()
513		for _, t := range types[:nt] {
514			cachedTypeFields(t)
515		}
516		b.Run(fmt.Sprintf("HitTypes%d", nt), func(b *testing.B) {
517			b.RunParallel(func(pb *testing.PB) {
518				for pb.Next() {
519					cachedTypeFields(types[0])
520				}
521			})
522		})
523	}
524}
525
526func BenchmarkEncodeMarshaler(b *testing.B) {
527	b.ReportAllocs()
528
529	m := struct {
530		A int
531		B RawMessage
532	}{}
533
534	b.RunParallel(func(pb *testing.PB) {
535		enc := NewEncoder(io.Discard)
536
537		for pb.Next() {
538			if err := enc.Encode(&m); err != nil {
539				b.Fatalf("Encode error: %v", err)
540			}
541		}
542	})
543}
544
545func BenchmarkEncoderEncode(b *testing.B) {
546	b.ReportAllocs()
547	type T struct {
548		X, Y string
549	}
550	v := &T{"foo", "bar"}
551	b.RunParallel(func(pb *testing.PB) {
552		for pb.Next() {
553			if err := NewEncoder(io.Discard).Encode(v); err != nil {
554				b.Fatalf("Encode error: %v", err)
555			}
556		}
557	})
558}
559
560func BenchmarkNumberIsValid(b *testing.B) {
561	s := "-61657.61667E+61673"
562	for i := 0; i < b.N; i++ {
563		isValidNumber(s)
564	}
565}
566
567func BenchmarkNumberIsValidRegexp(b *testing.B) {
568	var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`)
569	s := "-61657.61667E+61673"
570	for i := 0; i < b.N; i++ {
571		jsonNumberRegexp.MatchString(s)
572	}
573}
574
575func BenchmarkUnmarshalNumber(b *testing.B) {
576	b.ReportAllocs()
577	data := []byte(`"-61657.61667E+61673"`)
578	var number Number
579	for i := 0; i < b.N; i++ {
580		if err := Unmarshal(data, &number); err != nil {
581			b.Fatal("Unmarshal:", err)
582		}
583	}
584}
585