codekingpro/portable-devtools
114k
1// Copyright 2011 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// 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 the7// go, webkit, and chromium open source projects.8// We benchmark converting between the JSON form9// and in-memory data structures.10 11//go:build !goexperiment.jsonv212 13package json14 15import (16 "bytes"17 "fmt"18 "internal/testenv"19 "internal/zstd"20 "io"21 "os"22 "reflect"23 "regexp"24 "runtime"25 "strings"26 "sync"27 "testing"28)29 30type codeResponse struct {31 Tree *codeNode `json:"tree"`32 Username string `json:"username"`33}34 35type codeNode struct {36 Name string `json:"name"`37 Kids []*codeNode `json:"kids"`38 CLWeight float64 `json:"cl_weight"`39 Touches int `json:"touches"`40 MinT int64 `json:"min_t"`41 MaxT int64 `json:"max_t"`42 MeanT int64 `json:"mean_t"`43}44 45var codeJSON []byte46var codeStruct codeResponse47 48func codeInit() {49 f, err := os.Open("internal/jsontest/testdata/golang_source.json.zst")50 if err != nil {51 panic(err)52 }53 defer f.Close()54 gz := zstd.NewReader(f)55 data, err := io.ReadAll(gz)56 if err != nil {57 panic(err)58 }59 60 codeJSON = data61 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 break78 }79 }80 panic("re-marshal code.json: different result")81 }82}83 84func 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.Fatalf("Encode error: %v", err)96 }97 }98 })99 b.SetBytes(int64(len(codeJSON)))100}101 102func BenchmarkCodeEncoderError(b *testing.B) {103 b.ReportAllocs()104 if codeJSON == nil {105 b.StopTimer()106 codeInit()107 b.StartTimer()108 }109 110 // Trigger an error in Marshal with cyclic data.111 type Dummy struct {112 Name string113 Next *Dummy114 }115 dummy := Dummy{Name: "Dummy"}116 dummy.Next = &dummy117 118 b.RunParallel(func(pb *testing.PB) {119 enc := NewEncoder(io.Discard)120 for pb.Next() {121 if err := enc.Encode(&codeStruct); err != nil {122 b.Fatalf("Encode error: %v", err)123 }124 if _, err := Marshal(dummy); err == nil {125 b.Fatal("Marshal error: got nil, want non-nil")126 }127 }128 })129 b.SetBytes(int64(len(codeJSON)))130}131 132func BenchmarkCodeMarshal(b *testing.B) {133 b.ReportAllocs()134 if codeJSON == nil {135 b.StopTimer()136 codeInit()137 b.StartTimer()138 }139 b.RunParallel(func(pb *testing.PB) {140 for pb.Next() {141 if _, err := Marshal(&codeStruct); err != nil {142 b.Fatalf("Marshal error: %v", err)143 }144 }145 })146 b.SetBytes(int64(len(codeJSON)))147}148 149func BenchmarkCodeMarshalError(b *testing.B) {150 b.ReportAllocs()151 if codeJSON == nil {152 b.StopTimer()153 codeInit()154 b.StartTimer()155 }156 157 // Trigger an error in Marshal with cyclic data.158 type Dummy struct {159 Name string160 Next *Dummy161 }162 dummy := Dummy{Name: "Dummy"}163 dummy.Next = &dummy164 165 b.RunParallel(func(pb *testing.PB) {166 for pb.Next() {167 if _, err := Marshal(&codeStruct); err != nil {168 b.Fatalf("Marshal error: %v", err)169 }170 if _, err := Marshal(dummy); err == nil {171 b.Fatal("Marshal error: got nil, want non-nil")172 }173 }174 })175 b.SetBytes(int64(len(codeJSON)))176}177 178func benchMarshalBytes(n int) func(*testing.B) {179 sample := []byte("hello world")180 // Use a struct pointer, to avoid an allocation when passing it as an181 // interface parameter to Marshal.182 v := &struct {183 Bytes []byte184 }{185 bytes.Repeat(sample, (n/len(sample))+1)[:n],186 }187 return func(b *testing.B) {188 for i := 0; i < b.N; i++ {189 if _, err := Marshal(v); err != nil {190 b.Fatalf("Marshal error: %v", err)191 }192 }193 }194}195 196func benchMarshalBytesError(n int) func(*testing.B) {197 sample := []byte("hello world")198 // Use a struct pointer, to avoid an allocation when passing it as an199 // interface parameter to Marshal.200 v := &struct {201 Bytes []byte202 }{203 bytes.Repeat(sample, (n/len(sample))+1)[:n],204 }205 206 // Trigger an error in Marshal with cyclic data.207 type Dummy struct {208 Name string209 Next *Dummy210 }211 dummy := Dummy{Name: "Dummy"}212 dummy.Next = &dummy213 214 return func(b *testing.B) {215 for i := 0; i < b.N; i++ {216 if _, err := Marshal(v); err != nil {217 b.Fatalf("Marshal error: %v", err)218 }219 if _, err := Marshal(dummy); err == nil {220 b.Fatal("Marshal error: got nil, want non-nil")221 }222 }223 }224}225 226func BenchmarkMarshalBytes(b *testing.B) {227 b.ReportAllocs()228 // 32 fits within encodeState.scratch.229 b.Run("32", benchMarshalBytes(32))230 // 256 doesn't fit in encodeState.scratch, but is small enough to231 // allocate and avoid the slower base64.NewEncoder.232 b.Run("256", benchMarshalBytes(256))233 // 4096 is large enough that we want to avoid allocating for it.234 b.Run("4096", benchMarshalBytes(4096))235}236 237func BenchmarkMarshalBytesError(b *testing.B) {238 b.ReportAllocs()239 // 32 fits within encodeState.scratch.240 b.Run("32", benchMarshalBytesError(32))241 // 256 doesn't fit in encodeState.scratch, but is small enough to242 // allocate and avoid the slower base64.NewEncoder.243 b.Run("256", benchMarshalBytesError(256))244 // 4096 is large enough that we want to avoid allocating for it.245 b.Run("4096", benchMarshalBytesError(4096))246}247 248func BenchmarkMarshalMap(b *testing.B) {249 b.ReportAllocs()250 m := map[string]int{251 "key3": 3,252 "key2": 2,253 "key1": 1,254 }255 b.RunParallel(func(pb *testing.PB) {256 for pb.Next() {257 if _, err := Marshal(m); err != nil {258 b.Fatal("Marshal:", err)259 }260 }261 })262}263 264func BenchmarkCodeDecoder(b *testing.B) {265 b.ReportAllocs()266 if codeJSON == nil {267 b.StopTimer()268 codeInit()269 b.StartTimer()270 }271 b.RunParallel(func(pb *testing.PB) {272 var buf bytes.Buffer273 dec := NewDecoder(&buf)274 var r codeResponse275 for pb.Next() {276 buf.Write(codeJSON)277 // hide EOF278 buf.WriteByte('\n')279 buf.WriteByte('\n')280 buf.WriteByte('\n')281 if err := dec.Decode(&r); err != nil {282 b.Fatalf("Decode error: %v", err)283 }284 }285 })286 b.SetBytes(int64(len(codeJSON)))287}288 289func BenchmarkUnicodeDecoder(b *testing.B) {290 b.ReportAllocs()291 j := []byte(`"\uD83D\uDE01"`)292 b.SetBytes(int64(len(j)))293 r := bytes.NewReader(j)294 dec := NewDecoder(r)295 var out string296 b.ResetTimer()297 for i := 0; i < b.N; i++ {298 if err := dec.Decode(&out); err != nil {299 b.Fatalf("Decode error: %v", err)300 }301 r.Seek(0, 0)302 }303}304 305func BenchmarkDecoderStream(b *testing.B) {306 b.ReportAllocs()307 b.StopTimer()308 var buf bytes.Buffer309 dec := NewDecoder(&buf)310 buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n")311 var x any312 if err := dec.Decode(&x); err != nil {313 b.Fatalf("Decode error: %v", err)314 }315 ones := strings.Repeat(" 1\n", 300000) + "\n\n\n"316 b.StartTimer()317 for i := 0; i < b.N; i++ {318 if i%300000 == 0 {319 buf.WriteString(ones)320 }321 x = nil322 switch err := dec.Decode(&x); {323 case err != nil:324 b.Fatalf("Decode error: %v", err)325 case x != 1.0:326 b.Fatalf("Decode: got %v want 1.0", i)327 }328 }329}330 331func BenchmarkCodeUnmarshal(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 for pb.Next() {340 var r codeResponse341 if err := Unmarshal(codeJSON, &r); err != nil {342 b.Fatalf("Unmarshal error: %v", err)343 }344 }345 })346 b.SetBytes(int64(len(codeJSON)))347}348 349func BenchmarkCodeUnmarshalReuse(b *testing.B) {350 b.ReportAllocs()351 if codeJSON == nil {352 b.StopTimer()353 codeInit()354 b.StartTimer()355 }356 b.RunParallel(func(pb *testing.PB) {357 var r codeResponse358 for pb.Next() {359 if err := Unmarshal(codeJSON, &r); err != nil {360 b.Fatalf("Unmarshal error: %v", err)361 }362 }363 })364 b.SetBytes(int64(len(codeJSON)))365}366 367func BenchmarkUnmarshalString(b *testing.B) {368 b.ReportAllocs()369 data := []byte(`"hello, world"`)370 b.RunParallel(func(pb *testing.PB) {371 var s string372 for pb.Next() {373 if err := Unmarshal(data, &s); err != nil {374 b.Fatalf("Unmarshal error: %v", err)375 }376 }377 })378}379 380func BenchmarkUnmarshalFloat64(b *testing.B) {381 b.ReportAllocs()382 data := []byte(`3.14`)383 b.RunParallel(func(pb *testing.PB) {384 var f float64385 for pb.Next() {386 if err := Unmarshal(data, &f); err != nil {387 b.Fatalf("Unmarshal error: %v", err)388 }389 }390 })391}392 393func BenchmarkUnmarshalInt64(b *testing.B) {394 b.ReportAllocs()395 data := []byte(`3`)396 b.RunParallel(func(pb *testing.PB) {397 var x int64398 for pb.Next() {399 if err := Unmarshal(data, &x); err != nil {400 b.Fatalf("Unmarshal error: %v", err)401 }402 }403 })404}405 406func BenchmarkUnmarshalMap(b *testing.B) {407 b.ReportAllocs()408 data := []byte(`{"key1":"value1","key2":"value2","key3":"value3"}`)409 b.RunParallel(func(pb *testing.PB) {410 x := make(map[string]string, 3)411 for pb.Next() {412 if err := Unmarshal(data, &x); err != nil {413 b.Fatalf("Unmarshal error: %v", err)414 }415 }416 })417}418 419func BenchmarkIssue10335(b *testing.B) {420 b.ReportAllocs()421 j := []byte(`{"a":{ }}`)422 b.RunParallel(func(pb *testing.PB) {423 var s struct{}424 for pb.Next() {425 if err := Unmarshal(j, &s); err != nil {426 b.Fatalf("Unmarshal error: %v", err)427 }428 }429 })430}431 432func BenchmarkIssue34127(b *testing.B) {433 b.ReportAllocs()434 j := struct {435 Bar string `json:"bar,string"`436 }{437 Bar: `foobar`,438 }439 b.RunParallel(func(pb *testing.PB) {440 for pb.Next() {441 if _, err := Marshal(&j); err != nil {442 b.Fatalf("Marshal error: %v", err)443 }444 }445 })446}447 448func BenchmarkUnmapped(b *testing.B) {449 b.ReportAllocs()450 j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`)451 b.RunParallel(func(pb *testing.PB) {452 var s struct{}453 for pb.Next() {454 if err := Unmarshal(j, &s); err != nil {455 b.Fatalf("Unmarshal error: %v", err)456 }457 }458 })459}460 461func BenchmarkTypeFieldsCache(b *testing.B) {462 b.ReportAllocs()463 var maxTypes int = 1e6464 if testenv.Builder() != "" {465 maxTypes = 1e3 // restrict cache sizes on builders466 }467 468 // Dynamically generate many new types.469 types := make([]reflect.Type, maxTypes)470 fs := []reflect.StructField{{471 Type: reflect.TypeFor[string](),472 Index: []int{0},473 }}474 for i := range types {475 fs[0].Name = fmt.Sprintf("TypeFieldsCache%d", i)476 types[i] = reflect.StructOf(fs)477 }478 479 // clearClear clears the cache. Other JSON operations, must not be running.480 clearCache := func() {481 fieldCache = sync.Map{}482 }483 484 // MissTypes tests the performance of repeated cache misses.485 // This measures the time to rebuild a cache of size nt.486 for nt := 1; nt <= maxTypes; nt *= 10 {487 ts := types[:nt]488 b.Run(fmt.Sprintf("MissTypes%d", nt), func(b *testing.B) {489 nc := runtime.GOMAXPROCS(0)490 for i := 0; i < b.N; i++ {491 clearCache()492 var wg sync.WaitGroup493 for j := 0; j < nc; j++ {494 wg.Add(1)495 go func(j int) {496 for _, t := range ts[(j*len(ts))/nc : ((j+1)*len(ts))/nc] {497 cachedTypeFields(t)498 }499 wg.Done()500 }(j)501 }502 wg.Wait()503 }504 })505 }506 507 // HitTypes tests the performance of repeated cache hits.508 // This measures the average time of each cache lookup.509 for nt := 1; nt <= maxTypes; nt *= 10 {510 // Pre-warm a cache of size nt.511 clearCache()512 for _, t := range types[:nt] {513 cachedTypeFields(t)514 }515 b.Run(fmt.Sprintf("HitTypes%d", nt), func(b *testing.B) {516 b.RunParallel(func(pb *testing.PB) {517 for pb.Next() {518 cachedTypeFields(types[0])519 }520 })521 })522 }523}524 525func BenchmarkEncodeMarshaler(b *testing.B) {526 b.ReportAllocs()527 528 m := struct {529 A int530 B RawMessage531 }{}532 533 b.RunParallel(func(pb *testing.PB) {534 enc := NewEncoder(io.Discard)535 536 for pb.Next() {537 if err := enc.Encode(&m); err != nil {538 b.Fatalf("Encode error: %v", err)539 }540 }541 })542}543 544func BenchmarkEncoderEncode(b *testing.B) {545 b.ReportAllocs()546 type T struct {547 X, Y string548 }549 v := &T{"foo", "bar"}550 b.RunParallel(func(pb *testing.PB) {551 for pb.Next() {552 if err := NewEncoder(io.Discard).Encode(v); err != nil {553 b.Fatalf("Encode error: %v", err)554 }555 }556 })557}558 559func BenchmarkNumberIsValid(b *testing.B) {560 s := "-61657.61667E+61673"561 for i := 0; i < b.N; i++ {562 isValidNumber(s)563 }564}565 566func BenchmarkNumberIsValidRegexp(b *testing.B) {567 var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`)568 s := "-61657.61667E+61673"569 for i := 0; i < b.N; i++ {570 jsonNumberRegexp.MatchString(s)571 }572}573 574func BenchmarkUnmarshalNumber(b *testing.B) {575 b.ReportAllocs()576 data := []byte(`"-61657.61667E+61673"`)577 var number Number578 for i := 0; i < b.N; i++ {579 if err := Unmarshal(data, &number); err != nil {580 b.Fatal("Unmarshal:", err)581 }582 }583}584 