github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/encoding/json/bench_test.go (about) 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 "regexp" 22 "runtime" 23 "strings" 24 "sync" 25 "testing" 26 ) 27 28 type codeResponse struct { 29 Tree *codeNode `json:"tree"` 30 Username string `json:"username"` 31 } 32 33 type 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 43 var codeJSON []byte 44 var codeStruct codeResponse 45 46 func 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 85 func 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.Fatal("Encode:", err) 97 } 98 } 99 }) 100 b.SetBytes(int64(len(codeJSON))) 101 } 102 103 func 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.Fatal("Encode:", err) 124 } 125 if _, err := Marshal(dummy); err == nil { 126 b.Fatal("expect an error here") 127 } 128 } 129 }) 130 b.SetBytes(int64(len(codeJSON))) 131 } 132 133 func 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.Fatal("Marshal:", err) 144 } 145 } 146 }) 147 b.SetBytes(int64(len(codeJSON))) 148 } 149 150 func 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.Fatal("Marshal:", err) 170 } 171 if _, err := Marshal(dummy); err == nil { 172 b.Fatal("expect an error here") 173 } 174 } 175 }) 176 b.SetBytes(int64(len(codeJSON))) 177 } 178 179 func 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.Fatal("Marshal:", err) 192 } 193 } 194 } 195 } 196 197 func 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.Fatal("Marshal:", err) 219 } 220 if _, err := Marshal(dummy); err == nil { 221 b.Fatal("expect an error here") 222 } 223 } 224 } 225 } 226 227 func 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 238 func 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 249 func BenchmarkCodeDecoder(b *testing.B) { 250 b.ReportAllocs() 251 if codeJSON == nil { 252 b.StopTimer() 253 codeInit() 254 b.StartTimer() 255 } 256 b.RunParallel(func(pb *testing.PB) { 257 var buf bytes.Buffer 258 dec := NewDecoder(&buf) 259 var r codeResponse 260 for pb.Next() { 261 buf.Write(codeJSON) 262 // hide EOF 263 buf.WriteByte('\n') 264 buf.WriteByte('\n') 265 buf.WriteByte('\n') 266 if err := dec.Decode(&r); err != nil { 267 b.Fatal("Decode:", err) 268 } 269 } 270 }) 271 b.SetBytes(int64(len(codeJSON))) 272 } 273 274 func BenchmarkUnicodeDecoder(b *testing.B) { 275 b.ReportAllocs() 276 j := []byte(`"\uD83D\uDE01"`) 277 b.SetBytes(int64(len(j))) 278 r := bytes.NewReader(j) 279 dec := NewDecoder(r) 280 var out string 281 b.ResetTimer() 282 for i := 0; i < b.N; i++ { 283 if err := dec.Decode(&out); err != nil { 284 b.Fatal("Decode:", err) 285 } 286 r.Seek(0, 0) 287 } 288 } 289 290 func BenchmarkDecoderStream(b *testing.B) { 291 b.ReportAllocs() 292 b.StopTimer() 293 var buf bytes.Buffer 294 dec := NewDecoder(&buf) 295 buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n") 296 var x any 297 if err := dec.Decode(&x); err != nil { 298 b.Fatal("Decode:", err) 299 } 300 ones := strings.Repeat(" 1\n", 300000) + "\n\n\n" 301 b.StartTimer() 302 for i := 0; i < b.N; i++ { 303 if i%300000 == 0 { 304 buf.WriteString(ones) 305 } 306 x = nil 307 if err := dec.Decode(&x); err != nil || x != 1.0 { 308 b.Fatalf("Decode: %v after %d", err, i) 309 } 310 } 311 } 312 313 func BenchmarkCodeUnmarshal(b *testing.B) { 314 b.ReportAllocs() 315 if codeJSON == nil { 316 b.StopTimer() 317 codeInit() 318 b.StartTimer() 319 } 320 b.RunParallel(func(pb *testing.PB) { 321 for pb.Next() { 322 var r codeResponse 323 if err := Unmarshal(codeJSON, &r); err != nil { 324 b.Fatal("Unmarshal:", err) 325 } 326 } 327 }) 328 b.SetBytes(int64(len(codeJSON))) 329 } 330 331 func BenchmarkCodeUnmarshalReuse(b *testing.B) { 332 b.ReportAllocs() 333 if codeJSON == nil { 334 b.StopTimer() 335 codeInit() 336 b.StartTimer() 337 } 338 b.RunParallel(func(pb *testing.PB) { 339 var r codeResponse 340 for pb.Next() { 341 if err := Unmarshal(codeJSON, &r); err != nil { 342 b.Fatal("Unmarshal:", err) 343 } 344 } 345 }) 346 b.SetBytes(int64(len(codeJSON))) 347 } 348 349 func BenchmarkUnmarshalString(b *testing.B) { 350 b.ReportAllocs() 351 data := []byte(`"hello, world"`) 352 b.RunParallel(func(pb *testing.PB) { 353 var s string 354 for pb.Next() { 355 if err := Unmarshal(data, &s); err != nil { 356 b.Fatal("Unmarshal:", err) 357 } 358 } 359 }) 360 } 361 362 func BenchmarkUnmarshalFloat64(b *testing.B) { 363 b.ReportAllocs() 364 data := []byte(`3.14`) 365 b.RunParallel(func(pb *testing.PB) { 366 var f float64 367 for pb.Next() { 368 if err := Unmarshal(data, &f); err != nil { 369 b.Fatal("Unmarshal:", err) 370 } 371 } 372 }) 373 } 374 375 func BenchmarkUnmarshalInt64(b *testing.B) { 376 b.ReportAllocs() 377 data := []byte(`3`) 378 b.RunParallel(func(pb *testing.PB) { 379 var x int64 380 for pb.Next() { 381 if err := Unmarshal(data, &x); err != nil { 382 b.Fatal("Unmarshal:", err) 383 } 384 } 385 }) 386 } 387 388 func BenchmarkIssue10335(b *testing.B) { 389 b.ReportAllocs() 390 j := []byte(`{"a":{ }}`) 391 b.RunParallel(func(pb *testing.PB) { 392 var s struct{} 393 for pb.Next() { 394 if err := Unmarshal(j, &s); err != nil { 395 b.Fatal(err) 396 } 397 } 398 }) 399 } 400 401 func BenchmarkIssue34127(b *testing.B) { 402 b.ReportAllocs() 403 j := struct { 404 Bar string `json:"bar,string"` 405 }{ 406 Bar: `foobar`, 407 } 408 b.RunParallel(func(pb *testing.PB) { 409 for pb.Next() { 410 if _, err := Marshal(&j); err != nil { 411 b.Fatal(err) 412 } 413 } 414 }) 415 } 416 417 func BenchmarkUnmapped(b *testing.B) { 418 b.ReportAllocs() 419 j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`) 420 b.RunParallel(func(pb *testing.PB) { 421 var s struct{} 422 for pb.Next() { 423 if err := Unmarshal(j, &s); err != nil { 424 b.Fatal(err) 425 } 426 } 427 }) 428 } 429 430 func BenchmarkTypeFieldsCache(b *testing.B) { 431 b.ReportAllocs() 432 var maxTypes int = 1e6 433 if testenv.Builder() != "" { 434 maxTypes = 1e3 // restrict cache sizes on builders 435 } 436 437 // Dynamically generate many new types. 438 types := make([]reflect.Type, maxTypes) 439 fs := []reflect.StructField{{ 440 Type: reflect.TypeOf(""), 441 Index: []int{0}, 442 }} 443 for i := range types { 444 fs[0].Name = fmt.Sprintf("TypeFieldsCache%d", i) 445 types[i] = reflect.StructOf(fs) 446 } 447 448 // clearClear clears the cache. Other JSON operations, must not be running. 449 clearCache := func() { 450 fieldCache = sync.Map{} 451 } 452 453 // MissTypes tests the performance of repeated cache misses. 454 // This measures the time to rebuild a cache of size nt. 455 for nt := 1; nt <= maxTypes; nt *= 10 { 456 ts := types[:nt] 457 b.Run(fmt.Sprintf("MissTypes%d", nt), func(b *testing.B) { 458 nc := runtime.GOMAXPROCS(0) 459 for i := 0; i < b.N; i++ { 460 clearCache() 461 var wg sync.WaitGroup 462 for j := 0; j < nc; j++ { 463 wg.Add(1) 464 go func(j int) { 465 for _, t := range ts[(j*len(ts))/nc : ((j+1)*len(ts))/nc] { 466 cachedTypeFields(t) 467 } 468 wg.Done() 469 }(j) 470 } 471 wg.Wait() 472 } 473 }) 474 } 475 476 // HitTypes tests the performance of repeated cache hits. 477 // This measures the average time of each cache lookup. 478 for nt := 1; nt <= maxTypes; nt *= 10 { 479 // Pre-warm a cache of size nt. 480 clearCache() 481 for _, t := range types[:nt] { 482 cachedTypeFields(t) 483 } 484 b.Run(fmt.Sprintf("HitTypes%d", nt), func(b *testing.B) { 485 b.RunParallel(func(pb *testing.PB) { 486 for pb.Next() { 487 cachedTypeFields(types[0]) 488 } 489 }) 490 }) 491 } 492 } 493 494 func BenchmarkEncodeMarshaler(b *testing.B) { 495 b.ReportAllocs() 496 497 m := struct { 498 A int 499 B RawMessage 500 }{} 501 502 b.RunParallel(func(pb *testing.PB) { 503 enc := NewEncoder(io.Discard) 504 505 for pb.Next() { 506 if err := enc.Encode(&m); err != nil { 507 b.Fatal("Encode:", err) 508 } 509 } 510 }) 511 } 512 513 func BenchmarkEncoderEncode(b *testing.B) { 514 b.ReportAllocs() 515 type T struct { 516 X, Y string 517 } 518 v := &T{"foo", "bar"} 519 b.RunParallel(func(pb *testing.PB) { 520 for pb.Next() { 521 if err := NewEncoder(io.Discard).Encode(v); err != nil { 522 b.Fatal(err) 523 } 524 } 525 }) 526 } 527 528 func BenchmarkNumberIsValid(b *testing.B) { 529 s := "-61657.61667E+61673" 530 for i := 0; i < b.N; i++ { 531 isValidNumber(s) 532 } 533 } 534 535 func BenchmarkNumberIsValidRegexp(b *testing.B) { 536 var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) 537 s := "-61657.61667E+61673" 538 for i := 0; i < b.N; i++ { 539 jsonNumberRegexp.MatchString(s) 540 } 541 }