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/ioutil"
19	"os"
20	"reflect"
21	"runtime"
22	"strings"
23	"sync"
24	"testing"
25)
26
27type codeResponse struct {
28	Tree     *codeNode `json:"tree"`
29	Username string    `json:"username"`
30}
31
32type codeNode struct {
33	Name     string      `json:"name"`
34	Kids     []*codeNode `json:"kids"`
35	CLWeight float64     `json:"cl_weight"`
36	Touches  int         `json:"touches"`
37	MinT     int64       `json:"min_t"`
38	MaxT     int64       `json:"max_t"`
39	MeanT    int64       `json:"mean_t"`
40}
41
42var codeJSON []byte
43var codeStruct codeResponse
44
45func codeInit() {
46	f, err := os.Open("testdata/code.json.gz")
47	if err != nil {
48		panic(err)
49	}
50	defer f.Close()
51	gz, err := gzip.NewReader(f)
52	if err != nil {
53		panic(err)
54	}
55	data, err := ioutil.ReadAll(gz)
56	if err != nil {
57		panic(err)
58	}
59
60	codeJSON = data
61
62	if err := Unmarshal(codeJSON, &codeStruct); err != nil {
63		panic("unmarshal code.json: " + err.Error())
64	}
65
66	if data, err = Marshal(&codeStruct); err != nil {
67		panic("marshal code.json: " + err.Error())
68	}
69
70	if !bytes.Equal(data, codeJSON) {
71		println("different lengths", len(data), len(codeJSON))
72		for i := 0; i < len(data) && i < len(codeJSON); i++ {
73			if data[i] != codeJSON[i] {
74				println("re-marshal: changed at byte", i)
75				println("orig: ", string(codeJSON[i-10:i+10]))
76				println("new: ", string(data[i-10:i+10]))
77				break
78			}
79		}
80		panic("re-marshal code.json: different result")
81	}
82}
83
84func BenchmarkCodeEncoder(b *testing.B) {
85	if codeJSON == nil {
86		b.StopTimer()
87		codeInit()
88		b.StartTimer()
89	}
90	b.RunParallel(func(pb *testing.PB) {
91		enc := NewEncoder(ioutil.Discard)
92		for pb.Next() {
93			if err := enc.Encode(&codeStruct); err != nil {
94				b.Fatal("Encode:", err)
95			}
96		}
97	})
98	b.SetBytes(int64(len(codeJSON)))
99}
100
101func BenchmarkCodeMarshal(b *testing.B) {
102	if codeJSON == nil {
103		b.StopTimer()
104		codeInit()
105		b.StartTimer()
106	}
107	b.RunParallel(func(pb *testing.PB) {
108		for pb.Next() {
109			if _, err := Marshal(&codeStruct); err != nil {
110				b.Fatal("Marshal:", err)
111			}
112		}
113	})
114	b.SetBytes(int64(len(codeJSON)))
115}
116
117func benchMarshalBytes(n int) func(*testing.B) {
118	sample := []byte("hello world")
119	// Use a struct pointer, to avoid an allocation when passing it as an
120	// interface parameter to Marshal.
121	v := &struct {
122		Bytes []byte
123	}{
124		bytes.Repeat(sample, (n/len(sample))+1)[:n],
125	}
126	return func(b *testing.B) {
127		for i := 0; i < b.N; i++ {
128			if _, err := Marshal(v); err != nil {
129				b.Fatal("Marshal:", err)
130			}
131		}
132	}
133}
134
135func BenchmarkMarshalBytes(b *testing.B) {
136	// 32 fits within encodeState.scratch.
137	b.Run("32", benchMarshalBytes(32))
138	// 256 doesn't fit in encodeState.scratch, but is small enough to
139	// allocate and avoid the slower base64.NewEncoder.
140	b.Run("256", benchMarshalBytes(256))
141	// 4096 is large enough that we want to avoid allocating for it.
142	b.Run("4096", benchMarshalBytes(4096))
143}
144
145func BenchmarkCodeDecoder(b *testing.B) {
146	if codeJSON == nil {
147		b.StopTimer()
148		codeInit()
149		b.StartTimer()
150	}
151	b.RunParallel(func(pb *testing.PB) {
152		var buf bytes.Buffer
153		dec := NewDecoder(&buf)
154		var r codeResponse
155		for pb.Next() {
156			buf.Write(codeJSON)
157			// hide EOF
158			buf.WriteByte('\n')
159			buf.WriteByte('\n')
160			buf.WriteByte('\n')
161			if err := dec.Decode(&r); err != nil {
162				b.Fatal("Decode:", err)
163			}
164		}
165	})
166	b.SetBytes(int64(len(codeJSON)))
167}
168
169func BenchmarkUnicodeDecoder(b *testing.B) {
170	j := []byte(`"\uD83D\uDE01"`)
171	b.SetBytes(int64(len(j)))
172	r := bytes.NewReader(j)
173	dec := NewDecoder(r)
174	var out string
175	b.ResetTimer()
176	for i := 0; i < b.N; i++ {
177		if err := dec.Decode(&out); err != nil {
178			b.Fatal("Decode:", err)
179		}
180		r.Seek(0, 0)
181	}
182}
183
184func BenchmarkDecoderStream(b *testing.B) {
185	b.StopTimer()
186	var buf bytes.Buffer
187	dec := NewDecoder(&buf)
188	buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n")
189	var x interface{}
190	if err := dec.Decode(&x); err != nil {
191		b.Fatal("Decode:", err)
192	}
193	ones := strings.Repeat(" 1\n", 300000) + "\n\n\n"
194	b.StartTimer()
195	for i := 0; i < b.N; i++ {
196		if i%300000 == 0 {
197			buf.WriteString(ones)
198		}
199		x = nil
200		if err := dec.Decode(&x); err != nil || x != 1.0 {
201			b.Fatalf("Decode: %v after %d", err, i)
202		}
203	}
204}
205
206func BenchmarkCodeUnmarshal(b *testing.B) {
207	if codeJSON == nil {
208		b.StopTimer()
209		codeInit()
210		b.StartTimer()
211	}
212	b.RunParallel(func(pb *testing.PB) {
213		for pb.Next() {
214			var r codeResponse
215			if err := Unmarshal(codeJSON, &r); err != nil {
216				b.Fatal("Unmarshal:", err)
217			}
218		}
219	})
220	b.SetBytes(int64(len(codeJSON)))
221}
222
223func BenchmarkCodeUnmarshalReuse(b *testing.B) {
224	if codeJSON == nil {
225		b.StopTimer()
226		codeInit()
227		b.StartTimer()
228	}
229	b.RunParallel(func(pb *testing.PB) {
230		var r codeResponse
231		for pb.Next() {
232			if err := Unmarshal(codeJSON, &r); err != nil {
233				b.Fatal("Unmarshal:", err)
234			}
235		}
236	})
237	// TODO(bcmills): Is there a missing b.SetBytes here?
238}
239
240func BenchmarkUnmarshalString(b *testing.B) {
241	data := []byte(`"hello, world"`)
242	b.RunParallel(func(pb *testing.PB) {
243		var s string
244		for pb.Next() {
245			if err := Unmarshal(data, &s); err != nil {
246				b.Fatal("Unmarshal:", err)
247			}
248		}
249	})
250}
251
252func BenchmarkUnmarshalFloat64(b *testing.B) {
253	data := []byte(`3.14`)
254	b.RunParallel(func(pb *testing.PB) {
255		var f float64
256		for pb.Next() {
257			if err := Unmarshal(data, &f); err != nil {
258				b.Fatal("Unmarshal:", err)
259			}
260		}
261	})
262}
263
264func BenchmarkUnmarshalInt64(b *testing.B) {
265	data := []byte(`3`)
266	b.RunParallel(func(pb *testing.PB) {
267		var x int64
268		for pb.Next() {
269			if err := Unmarshal(data, &x); err != nil {
270				b.Fatal("Unmarshal:", err)
271			}
272		}
273	})
274}
275
276func BenchmarkIssue10335(b *testing.B) {
277	b.ReportAllocs()
278	j := []byte(`{"a":{ }}`)
279	b.RunParallel(func(pb *testing.PB) {
280		var s struct{}
281		for pb.Next() {
282			if err := Unmarshal(j, &s); err != nil {
283				b.Fatal(err)
284			}
285		}
286	})
287}
288
289func BenchmarkUnmapped(b *testing.B) {
290	b.ReportAllocs()
291	j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`)
292	b.RunParallel(func(pb *testing.PB) {
293		var s struct{}
294		for pb.Next() {
295			if err := Unmarshal(j, &s); err != nil {
296				b.Fatal(err)
297			}
298		}
299	})
300}
301
302func BenchmarkTypeFieldsCache(b *testing.B) {
303	var maxTypes int = 1e6
304	if testenv.Builder() != "" {
305		maxTypes = 1e3 // restrict cache sizes on builders
306	}
307
308	// Dynamically generate many new types.
309	types := make([]reflect.Type, maxTypes)
310	fs := []reflect.StructField{{
311		Type:  reflect.TypeOf(""),
312		Index: []int{0},
313	}}
314	for i := range types {
315		fs[0].Name = fmt.Sprintf("TypeFieldsCache%d", i)
316		types[i] = reflect.StructOf(fs)
317	}
318
319	// clearClear clears the cache. Other JSON operations, must not be running.
320	clearCache := func() {
321		fieldCache = sync.Map{}
322	}
323
324	// MissTypes tests the performance of repeated cache misses.
325	// This measures the time to rebuild a cache of size nt.
326	for nt := 1; nt <= maxTypes; nt *= 10 {
327		ts := types[:nt]
328		b.Run(fmt.Sprintf("MissTypes%d", nt), func(b *testing.B) {
329			nc := runtime.GOMAXPROCS(0)
330			for i := 0; i < b.N; i++ {
331				clearCache()
332				var wg sync.WaitGroup
333				for j := 0; j < nc; j++ {
334					wg.Add(1)
335					go func(j int) {
336						for _, t := range ts[(j*len(ts))/nc : ((j+1)*len(ts))/nc] {
337							cachedTypeFields(t)
338						}
339						wg.Done()
340					}(j)
341				}
342				wg.Wait()
343			}
344		})
345	}
346
347	// HitTypes tests the performance of repeated cache hits.
348	// This measures the average time of each cache lookup.
349	for nt := 1; nt <= maxTypes; nt *= 10 {
350		// Pre-warm a cache of size nt.
351		clearCache()
352		for _, t := range types[:nt] {
353			cachedTypeFields(t)
354		}
355		b.Run(fmt.Sprintf("HitTypes%d", nt), func(b *testing.B) {
356			b.RunParallel(func(pb *testing.PB) {
357				for pb.Next() {
358					cachedTypeFields(types[0])
359				}
360			})
361		})
362	}
363}
364