github.com/vmware/govmomi@v0.43.0/vim25/json/bench_test.go (about) 1 // +build ignore 2 3 // Copyright 2011 The Go Authors. All rights reserved. 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file. 6 7 // Large data benchmark. 8 // The JSON data is a summary of agl's changes in the 9 // go, webkit, and chromium open source projects. 10 // We benchmark converting between the JSON form 11 // and in-memory data structures. 12 13 package json 14 15 import ( 16 "bytes" 17 "compress/gzip" 18 "fmt" 19 "internal/testenv" 20 "io" 21 "os" 22 "reflect" 23 "runtime" 24 "strings" 25 "sync" 26 "testing" 27 ) 28 29 type codeResponse struct { 30 Tree *codeNode `json:"tree"` 31 Username string `json:"username"` 32 } 33 34 type codeNode struct { 35 Name string `json:"name"` 36 Kids []*codeNode `json:"kids"` 37 CLWeight float64 `json:"cl_weight"` 38 Touches int `json:"touches"` 39 MinT int64 `json:"min_t"` 40 MaxT int64 `json:"max_t"` 41 MeanT int64 `json:"mean_t"` 42 } 43 44 var codeJSON []byte 45 var codeStruct codeResponse 46 47 func codeInit() { 48 f, err := os.Open("testdata/code.json.gz") 49 if err != nil { 50 panic(err) 51 } 52 defer f.Close() 53 gz, err := gzip.NewReader(f) 54 if err != nil { 55 panic(err) 56 } 57 data, err := io.ReadAll(gz) 58 if err != nil { 59 panic(err) 60 } 61 62 codeJSON = data 63 64 if err := Unmarshal(codeJSON, &codeStruct); err != nil { 65 panic("unmarshal code.json: " + err.Error()) 66 } 67 68 if data, err = Marshal(&codeStruct); err != nil { 69 panic("marshal code.json: " + err.Error()) 70 } 71 72 if !bytes.Equal(data, codeJSON) { 73 println("different lengths", len(data), len(codeJSON)) 74 for i := 0; i < len(data) && i < len(codeJSON); i++ { 75 if data[i] != codeJSON[i] { 76 println("re-marshal: changed at byte", i) 77 println("orig: ", string(codeJSON[i-10:i+10])) 78 println("new: ", string(data[i-10:i+10])) 79 break 80 } 81 } 82 panic("re-marshal code.json: different result") 83 } 84 } 85 86 func BenchmarkCodeEncoder(b *testing.B) { 87 b.ReportAllocs() 88 if codeJSON == nil { 89 b.StopTimer() 90 codeInit() 91 b.StartTimer() 92 } 93 b.RunParallel(func(pb *testing.PB) { 94 enc := NewEncoder(io.Discard) 95 for pb.Next() { 96 if err := enc.Encode(&codeStruct); err != nil { 97 b.Fatal("Encode:", err) 98 } 99 } 100 }) 101 b.SetBytes(int64(len(codeJSON))) 102 } 103 104 func BenchmarkCodeMarshal(b *testing.B) { 105 b.ReportAllocs() 106 if codeJSON == nil { 107 b.StopTimer() 108 codeInit() 109 b.StartTimer() 110 } 111 b.RunParallel(func(pb *testing.PB) { 112 for pb.Next() { 113 if _, err := Marshal(&codeStruct); err != nil { 114 b.Fatal("Marshal:", err) 115 } 116 } 117 }) 118 b.SetBytes(int64(len(codeJSON))) 119 } 120 121 func benchMarshalBytes(n int) func(*testing.B) { 122 sample := []byte("hello world") 123 // Use a struct pointer, to avoid an allocation when passing it as an 124 // interface parameter to Marshal. 125 v := &struct { 126 Bytes []byte 127 }{ 128 bytes.Repeat(sample, (n/len(sample))+1)[:n], 129 } 130 return func(b *testing.B) { 131 for i := 0; i < b.N; i++ { 132 if _, err := Marshal(v); err != nil { 133 b.Fatal("Marshal:", err) 134 } 135 } 136 } 137 } 138 139 func BenchmarkMarshalBytes(b *testing.B) { 140 b.ReportAllocs() 141 // 32 fits within encodeState.scratch. 142 b.Run("32", benchMarshalBytes(32)) 143 // 256 doesn't fit in encodeState.scratch, but is small enough to 144 // allocate and avoid the slower base64.NewEncoder. 145 b.Run("256", benchMarshalBytes(256)) 146 // 4096 is large enough that we want to avoid allocating for it. 147 b.Run("4096", benchMarshalBytes(4096)) 148 } 149 150 func BenchmarkCodeDecoder(b *testing.B) { 151 b.ReportAllocs() 152 if codeJSON == nil { 153 b.StopTimer() 154 codeInit() 155 b.StartTimer() 156 } 157 b.RunParallel(func(pb *testing.PB) { 158 var buf bytes.Buffer 159 dec := NewDecoder(&buf) 160 var r codeResponse 161 for pb.Next() { 162 buf.Write(codeJSON) 163 // hide EOF 164 buf.WriteByte('\n') 165 buf.WriteByte('\n') 166 buf.WriteByte('\n') 167 if err := dec.Decode(&r); err != nil { 168 b.Fatal("Decode:", err) 169 } 170 } 171 }) 172 b.SetBytes(int64(len(codeJSON))) 173 } 174 175 func BenchmarkUnicodeDecoder(b *testing.B) { 176 b.ReportAllocs() 177 j := []byte(`"\uD83D\uDE01"`) 178 b.SetBytes(int64(len(j))) 179 r := bytes.NewReader(j) 180 dec := NewDecoder(r) 181 var out string 182 b.ResetTimer() 183 for i := 0; i < b.N; i++ { 184 if err := dec.Decode(&out); err != nil { 185 b.Fatal("Decode:", err) 186 } 187 r.Seek(0, 0) 188 } 189 } 190 191 func BenchmarkDecoderStream(b *testing.B) { 192 b.ReportAllocs() 193 b.StopTimer() 194 var buf bytes.Buffer 195 dec := NewDecoder(&buf) 196 buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n") 197 var x interface{} 198 if err := dec.Decode(&x); err != nil { 199 b.Fatal("Decode:", err) 200 } 201 ones := strings.Repeat(" 1\n", 300000) + "\n\n\n" 202 b.StartTimer() 203 for i := 0; i < b.N; i++ { 204 if i%300000 == 0 { 205 buf.WriteString(ones) 206 } 207 x = nil 208 if err := dec.Decode(&x); err != nil || x != 1.0 { 209 b.Fatalf("Decode: %v after %d", err, i) 210 } 211 } 212 } 213 214 func BenchmarkCodeUnmarshal(b *testing.B) { 215 b.ReportAllocs() 216 if codeJSON == nil { 217 b.StopTimer() 218 codeInit() 219 b.StartTimer() 220 } 221 b.RunParallel(func(pb *testing.PB) { 222 for pb.Next() { 223 var r codeResponse 224 if err := Unmarshal(codeJSON, &r); err != nil { 225 b.Fatal("Unmarshal:", err) 226 } 227 } 228 }) 229 b.SetBytes(int64(len(codeJSON))) 230 } 231 232 func BenchmarkCodeUnmarshalReuse(b *testing.B) { 233 b.ReportAllocs() 234 if codeJSON == nil { 235 b.StopTimer() 236 codeInit() 237 b.StartTimer() 238 } 239 b.RunParallel(func(pb *testing.PB) { 240 var r codeResponse 241 for pb.Next() { 242 if err := Unmarshal(codeJSON, &r); err != nil { 243 b.Fatal("Unmarshal:", err) 244 } 245 } 246 }) 247 b.SetBytes(int64(len(codeJSON))) 248 } 249 250 func BenchmarkUnmarshalString(b *testing.B) { 251 b.ReportAllocs() 252 data := []byte(`"hello, world"`) 253 b.RunParallel(func(pb *testing.PB) { 254 var s string 255 for pb.Next() { 256 if err := Unmarshal(data, &s); err != nil { 257 b.Fatal("Unmarshal:", err) 258 } 259 } 260 }) 261 } 262 263 func BenchmarkUnmarshalFloat64(b *testing.B) { 264 b.ReportAllocs() 265 data := []byte(`3.14`) 266 b.RunParallel(func(pb *testing.PB) { 267 var f float64 268 for pb.Next() { 269 if err := Unmarshal(data, &f); err != nil { 270 b.Fatal("Unmarshal:", err) 271 } 272 } 273 }) 274 } 275 276 func BenchmarkUnmarshalInt64(b *testing.B) { 277 b.ReportAllocs() 278 data := []byte(`3`) 279 b.RunParallel(func(pb *testing.PB) { 280 var x int64 281 for pb.Next() { 282 if err := Unmarshal(data, &x); err != nil { 283 b.Fatal("Unmarshal:", err) 284 } 285 } 286 }) 287 } 288 289 func BenchmarkIssue10335(b *testing.B) { 290 b.ReportAllocs() 291 j := []byte(`{"a":{ }}`) 292 b.RunParallel(func(pb *testing.PB) { 293 var s struct{} 294 for pb.Next() { 295 if err := Unmarshal(j, &s); err != nil { 296 b.Fatal(err) 297 } 298 } 299 }) 300 } 301 302 func BenchmarkIssue34127(b *testing.B) { 303 b.ReportAllocs() 304 j := struct { 305 Bar string `json:"bar,string"` 306 }{ 307 Bar: `foobar`, 308 } 309 b.RunParallel(func(pb *testing.PB) { 310 for pb.Next() { 311 if _, err := Marshal(&j); err != nil { 312 b.Fatal(err) 313 } 314 } 315 }) 316 } 317 318 func BenchmarkUnmapped(b *testing.B) { 319 b.ReportAllocs() 320 j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`) 321 b.RunParallel(func(pb *testing.PB) { 322 var s struct{} 323 for pb.Next() { 324 if err := Unmarshal(j, &s); err != nil { 325 b.Fatal(err) 326 } 327 } 328 }) 329 } 330 331 func BenchmarkTypeFieldsCache(b *testing.B) { 332 b.ReportAllocs() 333 var maxTypes int = 1e6 334 if testenv.Builder() != "" { 335 maxTypes = 1e3 // restrict cache sizes on builders 336 } 337 338 // Dynamically generate many new types. 339 types := make([]reflect.Type, maxTypes) 340 fs := []reflect.StructField{{ 341 Type: reflect.TypeOf(""), 342 Index: []int{0}, 343 }} 344 for i := range types { 345 fs[0].Name = fmt.Sprintf("TypeFieldsCache%d", i) 346 types[i] = reflect.StructOf(fs) 347 } 348 349 // clearClear clears the cache. Other JSON operations, must not be running. 350 clearCache := func() { 351 fieldCache = sync.Map{} 352 } 353 354 // MissTypes tests the performance of repeated cache misses. 355 // This measures the time to rebuild a cache of size nt. 356 for nt := 1; nt <= maxTypes; nt *= 10 { 357 ts := types[:nt] 358 b.Run(fmt.Sprintf("MissTypes%d", nt), func(b *testing.B) { 359 nc := runtime.GOMAXPROCS(0) 360 for i := 0; i < b.N; i++ { 361 clearCache() 362 var wg sync.WaitGroup 363 for j := 0; j < nc; j++ { 364 wg.Add(1) 365 go func(j int) { 366 for _, t := range ts[(j*len(ts))/nc : ((j+1)*len(ts))/nc] { 367 cachedTypeFields(t) 368 } 369 wg.Done() 370 }(j) 371 } 372 wg.Wait() 373 } 374 }) 375 } 376 377 // HitTypes tests the performance of repeated cache hits. 378 // This measures the average time of each cache lookup. 379 for nt := 1; nt <= maxTypes; nt *= 10 { 380 // Pre-warm a cache of size nt. 381 clearCache() 382 for _, t := range types[:nt] { 383 cachedTypeFields(t) 384 } 385 b.Run(fmt.Sprintf("HitTypes%d", nt), func(b *testing.B) { 386 b.RunParallel(func(pb *testing.PB) { 387 for pb.Next() { 388 cachedTypeFields(types[0]) 389 } 390 }) 391 }) 392 } 393 } 394 395 func BenchmarkEncodeMarshaler(b *testing.B) { 396 b.ReportAllocs() 397 398 m := struct { 399 A int 400 B RawMessage 401 }{} 402 403 b.RunParallel(func(pb *testing.PB) { 404 enc := NewEncoder(io.Discard) 405 406 for pb.Next() { 407 if err := enc.Encode(&m); err != nil { 408 b.Fatal("Encode:", err) 409 } 410 } 411 }) 412 }