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