gitee.com/quant1x/gox@v1.7.6/fastjson/parser_timing_test.go (about) 1 package fastjson 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "strings" 8 "testing" 9 ) 10 11 func BenchmarkParseRawString(b *testing.B) { 12 for _, s := range []string{`""`, `"a"`, `"abcd"`, `"abcdefghijk"`, `"qwertyuiopasdfghjklzxcvb"`} { 13 b.Run(s, func(b *testing.B) { 14 benchmarkParseRawString(b, s) 15 }) 16 } 17 } 18 19 func benchmarkParseRawString(b *testing.B, s string) { 20 b.ReportAllocs() 21 b.SetBytes(int64(len(s))) 22 s = s[1:] // skip the opening '"' 23 b.RunParallel(func(pb *testing.PB) { 24 for pb.Next() { 25 rs, tail, err := parseRawString(s) 26 if err != nil { 27 panic(fmt.Errorf("cannot parse %q: %s", s, err)) 28 } 29 if rs != s[:len(s)-1] { 30 panic(fmt.Errorf("invalid string obtained; got %q; want %q", rs, s[:len(s)-1])) 31 } 32 if len(tail) > 0 { 33 panic(fmt.Errorf("non-empty tail got: %q", tail)) 34 } 35 } 36 }) 37 } 38 39 func BenchmarkParseRawNumber(b *testing.B) { 40 for _, s := range []string{"1", "1234", "123456", "-1234", "1234567890.1234567", "-1.32434e+12"} { 41 b.Run(s, func(b *testing.B) { 42 benchmarkParseRawNumber(b, s) 43 }) 44 } 45 } 46 47 func benchmarkParseRawNumber(b *testing.B, s string) { 48 b.ReportAllocs() 49 b.SetBytes(int64(len(s))) 50 b.RunParallel(func(pb *testing.PB) { 51 for pb.Next() { 52 rn, tail, err := parseRawNumber(s) 53 if err != nil { 54 panic(fmt.Errorf("cannot parse %q: %s", s, err)) 55 } 56 if rn != s { 57 panic(fmt.Errorf("invalid number obtained; got %q; want %q", rn, s)) 58 } 59 if len(tail) > 0 { 60 panic(fmt.Errorf("non-empty tail got: %q", tail)) 61 } 62 } 63 }) 64 } 65 66 func BenchmarkObjectGet(b *testing.B) { 67 for _, itemsCount := range []int{10, 100, 1000, 10000, 100000} { 68 b.Run(fmt.Sprintf("items_%d", itemsCount), func(b *testing.B) { 69 for _, lookupsCount := range []int{0, 1, 2, 4, 8, 16, 32, 64} { 70 b.Run(fmt.Sprintf("lookups_%d", lookupsCount), func(b *testing.B) { 71 benchmarkObjectGet(b, itemsCount, lookupsCount) 72 }) 73 } 74 }) 75 } 76 } 77 78 func benchmarkObjectGet(b *testing.B, itemsCount, lookupsCount int) { 79 b.StopTimer() 80 var ss []string 81 for i := 0; i < itemsCount; i++ { 82 s := fmt.Sprintf(`"key_%d": "value_%d"`, i, i) 83 ss = append(ss, s) 84 } 85 s := "{" + strings.Join(ss, ",") + "}" 86 key := fmt.Sprintf("key_%d", len(ss)/2) 87 expectedValue := fmt.Sprintf("value_%d", len(ss)/2) 88 b.StartTimer() 89 b.ReportAllocs() 90 b.SetBytes(int64(len(s))) 91 92 b.RunParallel(func(pb *testing.PB) { 93 p := benchPool.Get() 94 for pb.Next() { 95 v, err := p.Parse(s) 96 if err != nil { 97 panic(fmt.Errorf("unexpected error: %s", err)) 98 } 99 o := v.GetObject() 100 for i := 0; i < lookupsCount; i++ { 101 sb := o.Get(key).GetStringBytes() 102 if string(sb) != expectedValue { 103 panic(fmt.Errorf("unexpected value; got %q; want %q", sb, expectedValue)) 104 } 105 } 106 } 107 benchPool.Put(p) 108 }) 109 } 110 111 func BenchmarkMarshalTo(b *testing.B) { 112 b.Run("small", func(b *testing.B) { 113 benchmarkMarshalTo(b, smallFixture) 114 }) 115 b.Run("medium", func(b *testing.B) { 116 benchmarkMarshalTo(b, mediumFixture) 117 }) 118 b.Run("large", func(b *testing.B) { 119 benchmarkMarshalTo(b, largeFixture) 120 }) 121 b.Run("canada", func(b *testing.B) { 122 benchmarkMarshalTo(b, canadaFixture) 123 }) 124 b.Run("citm", func(b *testing.B) { 125 benchmarkMarshalTo(b, citmFixture) 126 }) 127 b.Run("twitter", func(b *testing.B) { 128 benchmarkMarshalTo(b, twitterFixture) 129 }) 130 } 131 132 func benchmarkMarshalTo(b *testing.B, s string) { 133 p := benchPool.Get() 134 v, err := p.Parse(s) 135 if err != nil { 136 panic(fmt.Errorf("unexpected error: %s", err)) 137 } 138 139 b.ReportAllocs() 140 b.SetBytes(int64(len(s))) 141 b.RunParallel(func(pb *testing.PB) { 142 var b []byte 143 for pb.Next() { 144 // It is ok calling v.MarshalTo from concurrent 145 // goroutines, since MarshalTo doesn't modify v. 146 b = v.MarshalTo(b[:0]) 147 } 148 }) 149 benchPool.Put(p) 150 } 151 152 func BenchmarkParse(b *testing.B) { 153 b.Run("small", func(b *testing.B) { 154 benchmarkParse(b, smallFixture) 155 }) 156 b.Run("medium", func(b *testing.B) { 157 benchmarkParse(b, mediumFixture) 158 }) 159 b.Run("large", func(b *testing.B) { 160 benchmarkParse(b, largeFixture) 161 }) 162 b.Run("canada", func(b *testing.B) { 163 benchmarkParse(b, canadaFixture) 164 }) 165 b.Run("citm", func(b *testing.B) { 166 benchmarkParse(b, citmFixture) 167 }) 168 b.Run("twitter", func(b *testing.B) { 169 benchmarkParse(b, twitterFixture) 170 }) 171 } 172 173 var ( 174 // small, medium and large fixtures are from https://github.com/buger/jsonparser/blob/f04e003e4115787c6272636780bc206e5ffad6c4/benchmark/benchmark.go 175 smallFixture = getFromFile("testdata/small.json") 176 mediumFixture = getFromFile("testdata/medium.json") 177 largeFixture = getFromFile("testdata/large.json") 178 179 // canada, citm and twitter fixtures are from https://github.com/serde-rs/json-benchmark/tree/0db02e043b3ae87dc5065e7acb8654c1f7670c43/data 180 canadaFixture = getFromFile("testdata/canada.json") 181 citmFixture = getFromFile("testdata/citm_catalog.json") 182 twitterFixture = getFromFile("testdata/twitter.json") 183 ) 184 185 func getFromFile(filename string) string { 186 data, err := ioutil.ReadFile(filename) 187 if err != nil { 188 panic(fmt.Errorf("cannot read %s: %s", filename, err)) 189 } 190 return string(data) 191 } 192 193 func benchmarkParse(b *testing.B, s string) { 194 b.Run("stdjson-map", func(b *testing.B) { 195 benchmarkStdJSONParseMap(b, s) 196 }) 197 b.Run("stdjson-struct", func(b *testing.B) { 198 benchmarkStdJSONParseStruct(b, s) 199 }) 200 b.Run("stdjson-empty-struct", func(b *testing.B) { 201 benchmarkStdJSONParseEmptyStruct(b, s) 202 }) 203 b.Run("fastjson", func(b *testing.B) { 204 benchmarkFastJSONParse(b, s) 205 }) 206 b.Run("fastjson-get", func(b *testing.B) { 207 benchmarkFastJSONParseGet(b, s) 208 }) 209 } 210 211 func benchmarkFastJSONParse(b *testing.B, s string) { 212 b.ReportAllocs() 213 b.SetBytes(int64(len(s))) 214 b.RunParallel(func(pb *testing.PB) { 215 p := benchPool.Get() 216 for pb.Next() { 217 v, err := p.Parse(s) 218 if err != nil { 219 panic(fmt.Errorf("unexpected error: %s", err)) 220 } 221 if v.Type() != TypeObject { 222 panic(fmt.Errorf("unexpected value type; got %s; want %s", v.Type(), TypeObject)) 223 } 224 } 225 benchPool.Put(p) 226 }) 227 } 228 229 func benchmarkFastJSONParseGet(b *testing.B, s string) { 230 b.ReportAllocs() 231 b.SetBytes(int64(len(s))) 232 b.RunParallel(func(pb *testing.PB) { 233 p := benchPool.Get() 234 var n int 235 for pb.Next() { 236 v, err := p.Parse(s) 237 if err != nil { 238 panic(fmt.Errorf("unexpected error: %s", err)) 239 } 240 n += v.GetInt("sid") 241 n += len(v.GetStringBytes("uuid")) 242 p := v.Get("person") 243 if p != nil { 244 n++ 245 } 246 c := v.Get("company") 247 if c != nil { 248 n++ 249 } 250 u := v.Get("users") 251 if u != nil { 252 n++ 253 } 254 a := v.GetArray("features") 255 n += len(a) 256 a = v.GetArray("topicSubTopics") 257 n += len(a) 258 o := v.Get("search_metadata") 259 if o != nil { 260 n++ 261 } 262 } 263 benchPool.Put(p) 264 }) 265 } 266 267 var benchPool ParserPool 268 269 func benchmarkStdJSONParseMap(b *testing.B, s string) { 270 b.ReportAllocs() 271 b.SetBytes(int64(len(s))) 272 bb := s2b(s) 273 b.RunParallel(func(pb *testing.PB) { 274 var m map[string]interface{} 275 for pb.Next() { 276 if err := json.Unmarshal(bb, &m); err != nil { 277 panic(fmt.Errorf("unexpected error: %s", err)) 278 } 279 } 280 }) 281 } 282 283 func benchmarkStdJSONParseStruct(b *testing.B, s string) { 284 b.ReportAllocs() 285 b.SetBytes(int64(len(s))) 286 bb := s2b(s) 287 b.RunParallel(func(pb *testing.PB) { 288 var m struct { 289 Sid int 290 UUID string 291 Person map[string]interface{} 292 Company map[string]interface{} 293 Users []interface{} 294 Features []map[string]interface{} 295 TopicSubTopics map[string]interface{} 296 SearchMetadata map[string]interface{} 297 } 298 for pb.Next() { 299 if err := json.Unmarshal(bb, &m); err != nil { 300 panic(fmt.Errorf("unexpected error: %s", err)) 301 } 302 } 303 }) 304 } 305 306 func benchmarkStdJSONParseEmptyStruct(b *testing.B, s string) { 307 b.ReportAllocs() 308 b.SetBytes(int64(len(s))) 309 bb := s2b(s) 310 b.RunParallel(func(pb *testing.PB) { 311 var m struct{} 312 for pb.Next() { 313 if err := json.Unmarshal(bb, &m); err != nil { 314 panic(fmt.Errorf("unexpected error: %s", err)) 315 } 316 } 317 }) 318 }