k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/internal/third_party/go-json-experiment/json/intern_test.go (about) 1 // Copyright 2022 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 package json 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "testing" 12 ) 13 14 func TestIntern(t *testing.T) { 15 var sc stringCache 16 const alphabet = "abcdefghijklmnopqrstuvwxyz" 17 for i := 0; i <= len(alphabet); i++ { 18 want := alphabet[i:] 19 if got := sc.make([]byte(want)); got != want { 20 t.Fatalf("make = %v, want %v", got, want) 21 } 22 } 23 for i := 0; i < 1000; i++ { 24 want := fmt.Sprintf("test%b", i) 25 if got := sc.make([]byte(want)); got != want { 26 t.Fatalf("make = %v, want %v", got, want) 27 } 28 } 29 } 30 31 var sink string 32 33 func BenchmarkIntern(b *testing.B) { 34 datasetStrings := func(name string) (out [][]byte) { 35 var data []byte 36 for _, ts := range jsonTestdata() { 37 if ts.name == name { 38 data = ts.data 39 } 40 } 41 dec := NewDecoder(bytes.NewReader(data)) 42 for { 43 k, n := dec.StackIndex(dec.StackDepth()) 44 isObjectName := k == '{' && n%2 == 0 45 tok, err := dec.ReadToken() 46 if err != nil { 47 if err == io.EOF { 48 break 49 } 50 b.Fatalf("ReadToken error: %v", err) 51 } 52 if tok.Kind() == '"' && !isObjectName { 53 out = append(out, []byte(tok.String())) 54 } 55 } 56 return out 57 } 58 59 tests := []struct { 60 label string 61 data [][]byte 62 }{ 63 // Best is the best case scenario where every string is the same. 64 {"Best", func() (out [][]byte) { 65 for i := 0; i < 1000; i++ { 66 out = append(out, []byte("hello, world!")) 67 } 68 return out 69 }()}, 70 71 // Repeat is a sequence of the same set of names repeated. 72 // This commonly occurs when unmarshaling a JSON array of JSON objects, 73 // where the set of all names is usually small. 74 {"Repeat", func() (out [][]byte) { 75 for i := 0; i < 100; i++ { 76 for _, s := range []string{"first_name", "last_name", "age", "address", "street_address", "city", "state", "postal_code", "phone_numbers", "gender"} { 77 out = append(out, []byte(s)) 78 } 79 } 80 return out 81 }()}, 82 83 // Synthea is all string values encountered in the Synthea FHIR dataset. 84 {"Synthea", datasetStrings("SyntheaFhir")}, 85 86 // Twitter is all string values encountered in the Twitter dataset. 87 {"Twitter", datasetStrings("TwitterStatus")}, 88 89 // Worst is the worst case scenario where every string is different 90 // resulting in wasted time looking up a string that will never match. 91 {"Worst", func() (out [][]byte) { 92 for i := 0; i < 1000; i++ { 93 out = append(out, []byte(fmt.Sprintf("%016x", i))) 94 } 95 return out 96 }()}, 97 } 98 99 for _, tt := range tests { 100 b.Run(tt.label, func(b *testing.B) { 101 // Alloc simply heap allocates each string. 102 // This provides an upper bound on the number of allocations. 103 b.Run("Alloc", func(b *testing.B) { 104 b.ReportAllocs() 105 for i := 0; i < b.N; i++ { 106 for _, b := range tt.data { 107 sink = string(b) 108 } 109 } 110 }) 111 // Cache interns strings using stringCache. 112 // We want to optimize for having a faster runtime than Alloc, 113 // and also keeping the number of allocations closer to GoMap. 114 b.Run("Cache", func(b *testing.B) { 115 b.ReportAllocs() 116 for i := 0; i < b.N; i++ { 117 var sc stringCache 118 for _, b := range tt.data { 119 sink = sc.make(b) 120 } 121 } 122 }) 123 // GoMap interns all strings in a simple Go map. 124 // This provides a lower bound on the number of allocations. 125 b.Run("GoMap", func(b *testing.B) { 126 b.ReportAllocs() 127 for i := 0; i < b.N; i++ { 128 m := make(map[string]string) 129 for _, b := range tt.data { 130 s, ok := m[string(b)] 131 if !ok { 132 s = string(b) 133 m[s] = s 134 } 135 sink = s 136 } 137 } 138 }) 139 }) 140 } 141 }