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