...

Source file src/encoding/json/bench_test.go

Documentation: encoding/json

		 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  
		11  package json
		12  
		13  import (
		14  	"bytes"
		15  	"compress/gzip"
		16  	"fmt"
		17  	"internal/testenv"
		18  	"io"
		19  	"os"
		20  	"reflect"
		21  	"runtime"
		22  	"strings"
		23  	"sync"
		24  	"testing"
		25  )
		26  
		27  type codeResponse struct {
		28  	Tree		 *codeNode `json:"tree"`
		29  	Username string		`json:"username"`
		30  }
		31  
		32  type 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  
		42  var codeJSON []byte
		43  var codeStruct codeResponse
		44  
		45  func 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 := io.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  
		84  func BenchmarkCodeEncoder(b *testing.B) {
		85  	b.ReportAllocs()
		86  	if codeJSON == nil {
		87  		b.StopTimer()
		88  		codeInit()
		89  		b.StartTimer()
		90  	}
		91  	b.RunParallel(func(pb *testing.PB) {
		92  		enc := NewEncoder(io.Discard)
		93  		for pb.Next() {
		94  			if err := enc.Encode(&codeStruct); err != nil {
		95  				b.Fatal("Encode:", err)
		96  			}
		97  		}
		98  	})
		99  	b.SetBytes(int64(len(codeJSON)))
	 100  }
	 101  
	 102  func BenchmarkCodeMarshal(b *testing.B) {
	 103  	b.ReportAllocs()
	 104  	if codeJSON == nil {
	 105  		b.StopTimer()
	 106  		codeInit()
	 107  		b.StartTimer()
	 108  	}
	 109  	b.RunParallel(func(pb *testing.PB) {
	 110  		for pb.Next() {
	 111  			if _, err := Marshal(&codeStruct); err != nil {
	 112  				b.Fatal("Marshal:", err)
	 113  			}
	 114  		}
	 115  	})
	 116  	b.SetBytes(int64(len(codeJSON)))
	 117  }
	 118  
	 119  func benchMarshalBytes(n int) func(*testing.B) {
	 120  	sample := []byte("hello world")
	 121  	// Use a struct pointer, to avoid an allocation when passing it as an
	 122  	// interface parameter to Marshal.
	 123  	v := &struct {
	 124  		Bytes []byte
	 125  	}{
	 126  		bytes.Repeat(sample, (n/len(sample))+1)[:n],
	 127  	}
	 128  	return func(b *testing.B) {
	 129  		for i := 0; i < b.N; i++ {
	 130  			if _, err := Marshal(v); err != nil {
	 131  				b.Fatal("Marshal:", err)
	 132  			}
	 133  		}
	 134  	}
	 135  }
	 136  
	 137  func BenchmarkMarshalBytes(b *testing.B) {
	 138  	b.ReportAllocs()
	 139  	// 32 fits within encodeState.scratch.
	 140  	b.Run("32", benchMarshalBytes(32))
	 141  	// 256 doesn't fit in encodeState.scratch, but is small enough to
	 142  	// allocate and avoid the slower base64.NewEncoder.
	 143  	b.Run("256", benchMarshalBytes(256))
	 144  	// 4096 is large enough that we want to avoid allocating for it.
	 145  	b.Run("4096", benchMarshalBytes(4096))
	 146  }
	 147  
	 148  func BenchmarkCodeDecoder(b *testing.B) {
	 149  	b.ReportAllocs()
	 150  	if codeJSON == nil {
	 151  		b.StopTimer()
	 152  		codeInit()
	 153  		b.StartTimer()
	 154  	}
	 155  	b.RunParallel(func(pb *testing.PB) {
	 156  		var buf bytes.Buffer
	 157  		dec := NewDecoder(&buf)
	 158  		var r codeResponse
	 159  		for pb.Next() {
	 160  			buf.Write(codeJSON)
	 161  			// hide EOF
	 162  			buf.WriteByte('\n')
	 163  			buf.WriteByte('\n')
	 164  			buf.WriteByte('\n')
	 165  			if err := dec.Decode(&r); err != nil {
	 166  				b.Fatal("Decode:", err)
	 167  			}
	 168  		}
	 169  	})
	 170  	b.SetBytes(int64(len(codeJSON)))
	 171  }
	 172  
	 173  func BenchmarkUnicodeDecoder(b *testing.B) {
	 174  	b.ReportAllocs()
	 175  	j := []byte(`"\uD83D\uDE01"`)
	 176  	b.SetBytes(int64(len(j)))
	 177  	r := bytes.NewReader(j)
	 178  	dec := NewDecoder(r)
	 179  	var out string
	 180  	b.ResetTimer()
	 181  	for i := 0; i < b.N; i++ {
	 182  		if err := dec.Decode(&out); err != nil {
	 183  			b.Fatal("Decode:", err)
	 184  		}
	 185  		r.Seek(0, 0)
	 186  	}
	 187  }
	 188  
	 189  func BenchmarkDecoderStream(b *testing.B) {
	 190  	b.ReportAllocs()
	 191  	b.StopTimer()
	 192  	var buf bytes.Buffer
	 193  	dec := NewDecoder(&buf)
	 194  	buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n")
	 195  	var x interface{}
	 196  	if err := dec.Decode(&x); err != nil {
	 197  		b.Fatal("Decode:", err)
	 198  	}
	 199  	ones := strings.Repeat(" 1\n", 300000) + "\n\n\n"
	 200  	b.StartTimer()
	 201  	for i := 0; i < b.N; i++ {
	 202  		if i%300000 == 0 {
	 203  			buf.WriteString(ones)
	 204  		}
	 205  		x = nil
	 206  		if err := dec.Decode(&x); err != nil || x != 1.0 {
	 207  			b.Fatalf("Decode: %v after %d", err, i)
	 208  		}
	 209  	}
	 210  }
	 211  
	 212  func BenchmarkCodeUnmarshal(b *testing.B) {
	 213  	b.ReportAllocs()
	 214  	if codeJSON == nil {
	 215  		b.StopTimer()
	 216  		codeInit()
	 217  		b.StartTimer()
	 218  	}
	 219  	b.RunParallel(func(pb *testing.PB) {
	 220  		for pb.Next() {
	 221  			var r codeResponse
	 222  			if err := Unmarshal(codeJSON, &r); err != nil {
	 223  				b.Fatal("Unmarshal:", err)
	 224  			}
	 225  		}
	 226  	})
	 227  	b.SetBytes(int64(len(codeJSON)))
	 228  }
	 229  
	 230  func BenchmarkCodeUnmarshalReuse(b *testing.B) {
	 231  	b.ReportAllocs()
	 232  	if codeJSON == nil {
	 233  		b.StopTimer()
	 234  		codeInit()
	 235  		b.StartTimer()
	 236  	}
	 237  	b.RunParallel(func(pb *testing.PB) {
	 238  		var r codeResponse
	 239  		for pb.Next() {
	 240  			if err := Unmarshal(codeJSON, &r); err != nil {
	 241  				b.Fatal("Unmarshal:", err)
	 242  			}
	 243  		}
	 244  	})
	 245  	b.SetBytes(int64(len(codeJSON)))
	 246  }
	 247  
	 248  func BenchmarkUnmarshalString(b *testing.B) {
	 249  	b.ReportAllocs()
	 250  	data := []byte(`"hello, world"`)
	 251  	b.RunParallel(func(pb *testing.PB) {
	 252  		var s string
	 253  		for pb.Next() {
	 254  			if err := Unmarshal(data, &s); err != nil {
	 255  				b.Fatal("Unmarshal:", err)
	 256  			}
	 257  		}
	 258  	})
	 259  }
	 260  
	 261  func BenchmarkUnmarshalFloat64(b *testing.B) {
	 262  	b.ReportAllocs()
	 263  	data := []byte(`3.14`)
	 264  	b.RunParallel(func(pb *testing.PB) {
	 265  		var f float64
	 266  		for pb.Next() {
	 267  			if err := Unmarshal(data, &f); err != nil {
	 268  				b.Fatal("Unmarshal:", err)
	 269  			}
	 270  		}
	 271  	})
	 272  }
	 273  
	 274  func BenchmarkUnmarshalInt64(b *testing.B) {
	 275  	b.ReportAllocs()
	 276  	data := []byte(`3`)
	 277  	b.RunParallel(func(pb *testing.PB) {
	 278  		var x int64
	 279  		for pb.Next() {
	 280  			if err := Unmarshal(data, &x); err != nil {
	 281  				b.Fatal("Unmarshal:", err)
	 282  			}
	 283  		}
	 284  	})
	 285  }
	 286  
	 287  func BenchmarkIssue10335(b *testing.B) {
	 288  	b.ReportAllocs()
	 289  	j := []byte(`{"a":{ }}`)
	 290  	b.RunParallel(func(pb *testing.PB) {
	 291  		var s struct{}
	 292  		for pb.Next() {
	 293  			if err := Unmarshal(j, &s); err != nil {
	 294  				b.Fatal(err)
	 295  			}
	 296  		}
	 297  	})
	 298  }
	 299  
	 300  func BenchmarkIssue34127(b *testing.B) {
	 301  	b.ReportAllocs()
	 302  	j := struct {
	 303  		Bar string `json:"bar,string"`
	 304  	}{
	 305  		Bar: `foobar`,
	 306  	}
	 307  	b.RunParallel(func(pb *testing.PB) {
	 308  		for pb.Next() {
	 309  			if _, err := Marshal(&j); err != nil {
	 310  				b.Fatal(err)
	 311  			}
	 312  		}
	 313  	})
	 314  }
	 315  
	 316  func BenchmarkUnmapped(b *testing.B) {
	 317  	b.ReportAllocs()
	 318  	j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`)
	 319  	b.RunParallel(func(pb *testing.PB) {
	 320  		var s struct{}
	 321  		for pb.Next() {
	 322  			if err := Unmarshal(j, &s); err != nil {
	 323  				b.Fatal(err)
	 324  			}
	 325  		}
	 326  	})
	 327  }
	 328  
	 329  func BenchmarkTypeFieldsCache(b *testing.B) {
	 330  	b.ReportAllocs()
	 331  	var maxTypes int = 1e6
	 332  	if testenv.Builder() != "" {
	 333  		maxTypes = 1e3 // restrict cache sizes on builders
	 334  	}
	 335  
	 336  	// Dynamically generate many new types.
	 337  	types := make([]reflect.Type, maxTypes)
	 338  	fs := []reflect.StructField{{
	 339  		Type:	reflect.TypeOf(""),
	 340  		Index: []int{0},
	 341  	}}
	 342  	for i := range types {
	 343  		fs[0].Name = fmt.Sprintf("TypeFieldsCache%d", i)
	 344  		types[i] = reflect.StructOf(fs)
	 345  	}
	 346  
	 347  	// clearClear clears the cache. Other JSON operations, must not be running.
	 348  	clearCache := func() {
	 349  		fieldCache = sync.Map{}
	 350  	}
	 351  
	 352  	// MissTypes tests the performance of repeated cache misses.
	 353  	// This measures the time to rebuild a cache of size nt.
	 354  	for nt := 1; nt <= maxTypes; nt *= 10 {
	 355  		ts := types[:nt]
	 356  		b.Run(fmt.Sprintf("MissTypes%d", nt), func(b *testing.B) {
	 357  			nc := runtime.GOMAXPROCS(0)
	 358  			for i := 0; i < b.N; i++ {
	 359  				clearCache()
	 360  				var wg sync.WaitGroup
	 361  				for j := 0; j < nc; j++ {
	 362  					wg.Add(1)
	 363  					go func(j int) {
	 364  						for _, t := range ts[(j*len(ts))/nc : ((j+1)*len(ts))/nc] {
	 365  							cachedTypeFields(t)
	 366  						}
	 367  						wg.Done()
	 368  					}(j)
	 369  				}
	 370  				wg.Wait()
	 371  			}
	 372  		})
	 373  	}
	 374  
	 375  	// HitTypes tests the performance of repeated cache hits.
	 376  	// This measures the average time of each cache lookup.
	 377  	for nt := 1; nt <= maxTypes; nt *= 10 {
	 378  		// Pre-warm a cache of size nt.
	 379  		clearCache()
	 380  		for _, t := range types[:nt] {
	 381  			cachedTypeFields(t)
	 382  		}
	 383  		b.Run(fmt.Sprintf("HitTypes%d", nt), func(b *testing.B) {
	 384  			b.RunParallel(func(pb *testing.PB) {
	 385  				for pb.Next() {
	 386  					cachedTypeFields(types[0])
	 387  				}
	 388  			})
	 389  		})
	 390  	}
	 391  }
	 392  
	 393  func BenchmarkEncodeMarshaler(b *testing.B) {
	 394  	b.ReportAllocs()
	 395  
	 396  	m := struct {
	 397  		A int
	 398  		B RawMessage
	 399  	}{}
	 400  
	 401  	b.RunParallel(func(pb *testing.PB) {
	 402  		enc := NewEncoder(io.Discard)
	 403  
	 404  		for pb.Next() {
	 405  			if err := enc.Encode(&m); err != nil {
	 406  				b.Fatal("Encode:", err)
	 407  			}
	 408  		}
	 409  	})
	 410  }
	 411  

View as plain text