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 benchmark
12
13import (
14	"bytes"
15	"compress/gzip"
16	"fmt"
17	"io"
18	"io/ioutil"
19	"os"
20	"strings"
21	"testing"
22
23	stdjson "encoding/json"
24
25	"github.com/goccy/go-json"
26	jsoniter "github.com/json-iterator/go"
27	segmentiojson "github.com/segmentio/encoding/json"
28	"github.com/wI2L/jettison"
29)
30
31type codeResponse struct {
32	Tree     *codeNode `json:"tree"`
33	Username string    `json:"username"`
34}
35
36type codeNode struct {
37	Name     string      `json:"name"`
38	Kids     []*codeNode `json:"kids"`
39	CLWeight float64     `json:"cl_weight"`
40	Touches  int         `json:"touches"`
41	MinT     int64       `json:"min_t"`
42	MaxT     int64       `json:"max_t"`
43	MeanT    int64       `json:"mean_t"`
44}
45
46var codeJSON []byte
47var codeStruct codeResponse
48
49func codeInit() {
50	f, err := os.Open("testdata/code.json.gz")
51	if err != nil {
52		panic(err)
53	}
54	defer f.Close()
55	gz, err := gzip.NewReader(f)
56	if err != nil {
57		panic(err)
58	}
59	data, err := ioutil.ReadAll(gz)
60	if err != nil {
61		panic(err)
62	}
63
64	codeJSON = data
65
66	if err := stdjson.Unmarshal(codeJSON, &codeStruct); err != nil {
67		panic("unmarshal code.json: " + err.Error())
68	}
69	{
70		stdjsonbytes, err := stdjson.Marshal(&codeStruct)
71		if err != nil {
72			panic("marshal code.json: " + err.Error())
73		}
74		jsonbytes, err := json.Marshal(&codeStruct)
75		if err != nil {
76			panic("marshal code.json: " + err.Error())
77		}
78		if len(stdjsonbytes) != len(jsonbytes) {
79			panic(fmt.Sprintf("stdjson = %d but go-json = %d", len(stdjsonbytes), len(jsonbytes)))
80		}
81	}
82	if _, err := json.Marshal(&codeStruct); err != nil {
83		panic("marshal code.json: " + err.Error())
84	}
85	if !bytes.Equal(data, codeJSON) {
86		println("different lengths", len(data), len(codeJSON))
87		for i := 0; i < len(data) && i < len(codeJSON); i++ {
88			if data[i] != codeJSON[i] {
89				println("re-marshal: changed at byte", i)
90				println("orig: ", string(codeJSON[i-10:i+10]))
91				println("new: ", string(data[i-10:i+10]))
92				break
93			}
94		}
95		panic("re-marshal code.json: different result")
96	}
97}
98
99func Benchmark_EncodeBigData_GoJson(b *testing.B) {
100	b.ReportAllocs()
101	if codeJSON == nil {
102		b.StopTimer()
103		codeInit()
104		b.StartTimer()
105	}
106	b.RunParallel(func(pb *testing.PB) {
107		enc := json.NewEncoder(ioutil.Discard)
108		for pb.Next() {
109			if err := enc.Encode(&codeStruct); err != nil {
110				b.Fatal("Encode:", err)
111			}
112		}
113	})
114	b.SetBytes(int64(len(codeJSON)))
115}
116
117func Benchmark_EncodeBigData_EncodingJson(b *testing.B) {
118	b.ReportAllocs()
119	if codeJSON == nil {
120		b.StopTimer()
121		codeInit()
122		b.StartTimer()
123	}
124	b.RunParallel(func(pb *testing.PB) {
125		enc := stdjson.NewEncoder(ioutil.Discard)
126		for pb.Next() {
127			if err := enc.Encode(&codeStruct); err != nil {
128				b.Fatal("Encode:", err)
129			}
130		}
131	})
132	b.SetBytes(int64(len(codeJSON)))
133}
134
135func Benchmark_EncodeBigData_JsonIter(b *testing.B) {
136	b.ReportAllocs()
137	if codeJSON == nil {
138		b.StopTimer()
139		codeInit()
140		b.StartTimer()
141	}
142	var json = jsoniter.ConfigCompatibleWithStandardLibrary
143	b.RunParallel(func(pb *testing.PB) {
144		enc := json.NewEncoder(ioutil.Discard)
145		for pb.Next() {
146			if err := enc.Encode(&codeStruct); err != nil {
147				b.Fatal("Encode:", err)
148			}
149		}
150	})
151	b.SetBytes(int64(len(codeJSON)))
152}
153
154func Benchmark_EncodeBigData_SegmentioJson(b *testing.B) {
155	b.ReportAllocs()
156	if codeJSON == nil {
157		b.StopTimer()
158		codeInit()
159		b.StartTimer()
160	}
161	b.RunParallel(func(pb *testing.PB) {
162		enc := segmentiojson.NewEncoder(ioutil.Discard)
163		for pb.Next() {
164			if err := enc.Encode(&codeStruct); err != nil {
165				b.Fatal("Encode:", err)
166			}
167		}
168	})
169	b.SetBytes(int64(len(codeJSON)))
170}
171
172func Benchmark_MarshalBigData_GoJson(b *testing.B) {
173	b.ReportAllocs()
174	if codeJSON == nil {
175		b.StopTimer()
176		codeInit()
177		b.StartTimer()
178	}
179
180	b.RunParallel(func(pb *testing.PB) {
181		for pb.Next() {
182			if _, err := json.Marshal(&codeStruct); err != nil {
183				b.Fatal("Marshal:", err)
184			}
185		}
186	})
187	b.SetBytes(int64(len(codeJSON)))
188}
189
190func Benchmark_MarshalBigData_EncodingJson(b *testing.B) {
191	b.ReportAllocs()
192	if codeJSON == nil {
193		b.StopTimer()
194		codeInit()
195		b.StartTimer()
196	}
197	b.RunParallel(func(pb *testing.PB) {
198		for pb.Next() {
199			if _, err := stdjson.Marshal(&codeStruct); err != nil {
200				b.Fatal("Marshal:", err)
201			}
202		}
203	})
204	b.SetBytes(int64(len(codeJSON)))
205}
206
207func Benchmark_MarshalBigData_JsonIter(b *testing.B) {
208	b.ReportAllocs()
209	if codeJSON == nil {
210		b.StopTimer()
211		codeInit()
212		b.StartTimer()
213	}
214	var json = jsoniter.ConfigCompatibleWithStandardLibrary
215	b.RunParallel(func(pb *testing.PB) {
216		for pb.Next() {
217			if _, err := json.Marshal(&codeStruct); err != nil {
218				b.Fatal("Marshal:", err)
219			}
220		}
221	})
222	b.SetBytes(int64(len(codeJSON)))
223}
224
225func Benchmark_MarshalBigData_Jettison(b *testing.B) {
226	b.ReportAllocs()
227	if codeJSON == nil {
228		b.StopTimer()
229		codeInit()
230		b.StartTimer()
231	}
232	b.RunParallel(func(pb *testing.PB) {
233		for pb.Next() {
234			if _, err := jettison.Marshal(&codeStruct); err != nil {
235				b.Fatal("Marshal:", err)
236			}
237		}
238	})
239	b.SetBytes(int64(len(codeJSON)))
240}
241
242func Benchmark_MarshalBigData_SegmentioJson(b *testing.B) {
243	b.ReportAllocs()
244	if codeJSON == nil {
245		b.StopTimer()
246		codeInit()
247		b.StartTimer()
248	}
249	b.RunParallel(func(pb *testing.PB) {
250		for pb.Next() {
251			if _, err := segmentiojson.Marshal(&codeStruct); err != nil {
252				b.Fatal("Marshal:", err)
253			}
254		}
255	})
256	b.SetBytes(int64(len(codeJSON)))
257}
258
259func benchMarshalBytes(n int, marshaler func(interface{}) ([]byte, error)) func(*testing.B) {
260	sample := []byte("hello world")
261	// Use a struct pointer, to avoid an allocation when passing it as an
262	// interface parameter to Marshal.
263	v := &struct {
264		Bytes []byte
265	}{
266		bytes.Repeat(sample, (n/len(sample))+1)[:n],
267	}
268	return func(b *testing.B) {
269		b.ReportAllocs()
270		for i := 0; i < b.N; i++ {
271			if _, err := marshaler(v); err != nil {
272				b.Fatal("Marshal:", err)
273			}
274		}
275	}
276}
277
278func Benchmark_MarshalBytes_EncodingJson(b *testing.B) {
279	// 32 fits within encodeState.scratch.
280	b.Run("32", benchMarshalBytes(32, stdjson.Marshal))
281	// 256 doesn't fit in encodeState.scratch, but is small enough to
282	// allocate and avoid the slower base64.NewEncoder.
283	b.Run("256", benchMarshalBytes(256, stdjson.Marshal))
284	// 4096 is large enough that we want to avoid allocating for it.
285	b.Run("4096", benchMarshalBytes(4096, stdjson.Marshal))
286}
287
288func Benchmark_MarshalBytes_JsonIter(b *testing.B) {
289	var json = jsoniter.ConfigCompatibleWithStandardLibrary
290	// 32 fits within encodeState.scratch.
291	b.Run("32", benchMarshalBytes(32, json.Marshal))
292	// 256 doesn't fit in encodeState.scratch, but is small enough to
293	// allocate and avoid the slower base64.NewEncoder.
294	b.Run("256", benchMarshalBytes(256, json.Marshal))
295	// 4096 is large enough that we want to avoid allocating for it.
296	b.Run("4096", benchMarshalBytes(4096, json.Marshal))
297}
298
299func Benchmark_MarshalBytes_GoJson(b *testing.B) {
300	b.ReportAllocs()
301	// 32 fits within encodeState.scratch.
302	b.Run("32", benchMarshalBytes(32, json.Marshal))
303	// 256 doesn't fit in encodeState.scratch, but is small enough to
304	// allocate and avoid the slower base64.NewEncoder.
305	b.Run("256", benchMarshalBytes(256, json.Marshal))
306	// 4096 is large enough that we want to avoid allocating for it.
307	b.Run("4096", benchMarshalBytes(4096, json.Marshal))
308}
309
310func Benchmark_EncodeRawMessage_EncodingJson(b *testing.B) {
311	b.ReportAllocs()
312
313	m := struct {
314		A int
315		B json.RawMessage
316	}{}
317
318	b.RunParallel(func(pb *testing.PB) {
319		enc := stdjson.NewEncoder(ioutil.Discard)
320		for pb.Next() {
321			if err := enc.Encode(&m); err != nil {
322				b.Fatal("Encode:", err)
323			}
324		}
325	})
326}
327
328func Benchmark_EncodeRawMessage_JsonIter(b *testing.B) {
329	b.ReportAllocs()
330
331	m := struct {
332		A int
333		B json.RawMessage
334	}{}
335
336	var json = jsoniter.ConfigCompatibleWithStandardLibrary
337
338	b.RunParallel(func(pb *testing.PB) {
339		enc := json.NewEncoder(ioutil.Discard)
340		for pb.Next() {
341			if err := enc.Encode(&m); err != nil {
342				b.Fatal("Encode:", err)
343			}
344		}
345	})
346}
347
348func Benchmark_EncodeRawMessage_GoJson(b *testing.B) {
349	b.ReportAllocs()
350
351	m := struct {
352		A int
353		B json.RawMessage
354	}{}
355	b.RunParallel(func(pb *testing.PB) {
356		enc := json.NewEncoder(ioutil.Discard)
357		for pb.Next() {
358			if err := enc.Encode(&m); err != nil {
359				b.Fatal("Encode:", err)
360			}
361		}
362	})
363}
364
365func Benchmark_MarshalString_EncodingJson(b *testing.B) {
366	b.ReportAllocs()
367	j := struct {
368		Bar string `json:"bar,string"`
369	}{
370		Bar: `foobar`,
371	}
372	b.RunParallel(func(pb *testing.PB) {
373		for pb.Next() {
374			if _, err := stdjson.Marshal(&j); err != nil {
375				b.Fatal(err)
376			}
377		}
378	})
379}
380
381func Benchmark_MarshalString_JsonIter(b *testing.B) {
382	b.ReportAllocs()
383	j := struct {
384		Bar string `json:"bar,string"`
385	}{
386		Bar: `foobar`,
387	}
388	var json = jsoniter.ConfigCompatibleWithStandardLibrary
389	b.RunParallel(func(pb *testing.PB) {
390		for pb.Next() {
391			if _, err := json.Marshal(&j); err != nil {
392				b.Fatal(err)
393			}
394		}
395	})
396}
397
398func Benchmark_MarshalString_GoJson(b *testing.B) {
399	b.ReportAllocs()
400	j := struct {
401		Bar string `json:"bar,string"`
402	}{
403		Bar: `foobar`,
404	}
405	b.RunParallel(func(pb *testing.PB) {
406		for pb.Next() {
407			if _, err := json.Marshal(&j); err != nil {
408				b.Fatal(err)
409			}
410		}
411	})
412}
413
414func BenchmarkCodeDecoder(b *testing.B) {
415	b.ReportAllocs()
416	if codeJSON == nil {
417		b.StopTimer()
418		codeInit()
419		b.StartTimer()
420	}
421	b.RunParallel(func(pb *testing.PB) {
422		var buf bytes.Buffer
423		dec := json.NewDecoder(&buf)
424		var r codeResponse
425		for pb.Next() {
426			buf.Write(codeJSON)
427			// hide EOF
428			buf.WriteByte('\n')
429			buf.WriteByte('\n')
430			buf.WriteByte('\n')
431			if err := dec.Decode(&r); err != nil {
432				if err != io.EOF {
433					b.Fatal("Decode:", err)
434				}
435			}
436		}
437	})
438	b.SetBytes(int64(len(codeJSON)))
439}
440
441func BenchmarkUnicodeDecoder(b *testing.B) {
442	b.ReportAllocs()
443	j := []byte(`"\uD83D\uDE01"`)
444	b.SetBytes(int64(len(j)))
445	r := bytes.NewReader(j)
446	dec := json.NewDecoder(r)
447	var out string
448	b.ResetTimer()
449	for i := 0; i < b.N; i++ {
450		if err := dec.Decode(&out); err != nil {
451			if err != io.EOF {
452				b.Fatal("Decode:", err)
453			}
454		}
455		r.Seek(0, 0)
456	}
457}
458
459func BenchmarkDecoderStream(b *testing.B) {
460	b.ReportAllocs()
461	b.StopTimer()
462	var buf bytes.Buffer
463	dec := json.NewDecoder(&buf)
464	buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n")
465	var x interface{}
466	if err := dec.Decode(&x); err != nil {
467		b.Fatal("Decode:", err)
468	}
469	ones := strings.Repeat(" 1\n", 300000) + "\n\n\n"
470	b.StartTimer()
471	for i := 0; i < b.N; i++ {
472		if i%300000 == 0 {
473			buf.WriteString(ones)
474		}
475		x = nil
476		if err := dec.Decode(&x); err != nil || x != 1.0 {
477			if err != io.EOF {
478				b.Fatalf("Decode: %v after %d", err, i)
479			}
480		}
481	}
482}
483
484func BenchmarkCodeUnmarshal(b *testing.B) {
485	b.ReportAllocs()
486	if codeJSON == nil {
487		b.StopTimer()
488		codeInit()
489		b.StartTimer()
490	}
491	b.RunParallel(func(pb *testing.PB) {
492		for pb.Next() {
493			var r codeResponse
494			if err := json.Unmarshal(codeJSON, &r); err != nil {
495				b.Fatal("Unmarshal:", err)
496			}
497		}
498	})
499	b.SetBytes(int64(len(codeJSON)))
500}
501
502func BenchmarkCodeUnmarshalReuse(b *testing.B) {
503	b.ReportAllocs()
504	if codeJSON == nil {
505		b.StopTimer()
506		codeInit()
507		b.StartTimer()
508	}
509	b.RunParallel(func(pb *testing.PB) {
510		var r codeResponse
511		for pb.Next() {
512			if err := json.Unmarshal(codeJSON, &r); err != nil {
513				b.Fatal("Unmarshal:", err)
514			}
515		}
516	})
517	b.SetBytes(int64(len(codeJSON)))
518}
519
520func BenchmarkUnmarshalString(b *testing.B) {
521	b.ReportAllocs()
522	data := []byte(`"hello, world"`)
523	b.RunParallel(func(pb *testing.PB) {
524		var s string
525		for pb.Next() {
526			if err := json.Unmarshal(data, &s); err != nil {
527				b.Fatal("Unmarshal:", err)
528			}
529		}
530	})
531}
532
533func BenchmarkUnmarshalFloat64(b *testing.B) {
534	b.ReportAllocs()
535	data := []byte(`3.14`)
536	b.RunParallel(func(pb *testing.PB) {
537		var f float64
538		for pb.Next() {
539			if err := json.Unmarshal(data, &f); err != nil {
540				b.Fatal("Unmarshal:", err)
541			}
542		}
543	})
544}
545
546func BenchmarkUnmarshalInt64(b *testing.B) {
547	b.ReportAllocs()
548	data := []byte(`3`)
549	b.RunParallel(func(pb *testing.PB) {
550		var x int64
551		for pb.Next() {
552			if err := json.Unmarshal(data, &x); err != nil {
553				b.Fatal("Unmarshal:", err)
554			}
555		}
556	})
557}
558
559func BenchmarkIssue10335(b *testing.B) {
560	b.ReportAllocs()
561	j := []byte(`{"a":{ }}`)
562	b.RunParallel(func(pb *testing.PB) {
563		var s struct{}
564		for pb.Next() {
565			if err := json.Unmarshal(j, &s); err != nil {
566				b.Fatal(err)
567			}
568		}
569	})
570}
571
572func BenchmarkUnmapped(b *testing.B) {
573	b.ReportAllocs()
574	j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`)
575	b.RunParallel(func(pb *testing.PB) {
576		var s struct{}
577		for pb.Next() {
578			if err := json.Unmarshal(j, &s); err != nil {
579				b.Fatal(err)
580			}
581		}
582	})
583}
584
585func Benchmark_Compact_EncodingJson(b *testing.B) {
586	b.ReportAllocs()
587	if codeJSON == nil {
588		b.StopTimer()
589		codeInit()
590		b.StartTimer()
591	}
592	for i := 0; i < b.N; i++ {
593		var buf bytes.Buffer
594		if err := stdjson.Compact(&buf, codeJSON); err != nil {
595			b.Fatal(err)
596		}
597	}
598}
599
600func Benchmark_Compact_GoJson(b *testing.B) {
601	b.ReportAllocs()
602	if codeJSON == nil {
603		b.StopTimer()
604		codeInit()
605		b.StartTimer()
606	}
607	for i := 0; i < b.N; i++ {
608		var buf bytes.Buffer
609		if err := json.Compact(&buf, codeJSON); err != nil {
610			b.Fatal(err)
611		}
612	}
613}
614
615func Benchmark_Indent_EncodingJson(b *testing.B) {
616	b.ReportAllocs()
617	if codeJSON == nil {
618		b.StopTimer()
619		codeInit()
620		b.StartTimer()
621	}
622	for i := 0; i < b.N; i++ {
623		var buf bytes.Buffer
624		if err := stdjson.Indent(&buf, codeJSON, "-", " "); err != nil {
625			b.Fatal(err)
626		}
627	}
628}
629
630func Benchmark_Indent_GoJson(b *testing.B) {
631	b.ReportAllocs()
632	if codeJSON == nil {
633		b.StopTimer()
634		codeInit()
635		b.StartTimer()
636	}
637	for i := 0; i < b.N; i++ {
638		var buf bytes.Buffer
639		if err := json.Indent(&buf, codeJSON, "-", " "); err != nil {
640			b.Fatal(err)
641		}
642	}
643}
644