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/ioutil" 19 "os" 20 "reflect" 21 "runtime" 22 "strings" 23 "sync" 24 "testing" 25) 26 27type codeResponse struct { 28 Tree *codeNode `json:"tree"` 29 Username string `json:"username"` 30} 31 32type 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 42var codeJSON []byte 43var codeStruct codeResponse 44 45func 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 := ioutil.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 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(ioutil.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 102func 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 119func 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 137func 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 148func 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 173func 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 189func 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 212func 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 230func 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 248func 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 261func 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 274func 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 287func 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 300func 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 316func 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 329func 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 393func 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(ioutil.Discard) 403 404 for pb.Next() { 405 if err := enc.Encode(&m); err != nil { 406 b.Fatal("Encode:", err) 407 } 408 } 409 }) 410} 411