github.com/code-reading/golang@v0.0.0-20220303082512-ba5bc0e589a3/go/src/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 "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 }