From 6daf3373dc29b7544de1d2a0314e5d3cb752ab85 Mon Sep 17 00:00:00 2001 From: Josh Baker Date: Mon, 14 Aug 2017 08:21:16 -0700 Subject: [PATCH] Moved benchmark code Moved benchmark to a different repository to avoid the fetching of unneeded imports. Please find these benchmarks at https://github.com/tidwall/gjson-benchmarks --- README.md | 3 +- gjson_test.go | 582 +------------------------------------------------- 2 files changed, 3 insertions(+), 582 deletions(-) diff --git a/README.md b/README.md index 79f3ec4..e56f042 100644 --- a/README.md +++ b/README.md @@ -407,7 +407,8 @@ widget.text.data widget.text.size ``` -*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.8.* +*These benchmarks were run on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.8 and can be be found [here](https://github.com/tidwall/gjson-benchmarks).* + ## Contact Josh Baker [@tidwall](http://twitter.com/tidwall) diff --git a/gjson_test.go b/gjson_test.go index 00ab1bd..48ade51 100644 --- a/gjson_test.go +++ b/gjson_test.go @@ -5,18 +5,11 @@ import ( "encoding/hex" "encoding/json" "fmt" - "io" "math/rand" "reflect" - "strconv" "strings" "testing" "time" - - "github.com/buger/jsonparser" - jsoniter "github.com/json-iterator/go" - "github.com/mailru/easyjson/jlexer" - fflib "github.com/pquerna/ffjson/fflib/v1" ) // TestRandomData is a fuzzing test that throws random data at the Parse @@ -229,6 +222,7 @@ func TestBasic(t *testing.T) { t.Fatalf("expected %v, got %v", `["Brett","Elliotte"]`, mtok.String()) } } + func TestPlus53BitInts(t *testing.T) { json := `{"IdentityData":{"GameInstanceId":634866135153775564}}` value := Get(json, "IdentityData.GameInstanceId") @@ -1050,577 +1044,3 @@ func TestValidRandom(t *testing.T) { validpayload(b[:n], 0) } } - -type BenchStruct struct { - Widget struct { - Window struct { - Name string `json:"name"` - } `json:"window"` - Image struct { - HOffset int `json:"hOffset"` - } `json:"image"` - Text struct { - OnMouseUp string `json:"onMouseUp"` - } `json:"text"` - } `json:"widget"` -} - -var benchPaths = []string{ - "widget.window.name", - "widget.image.hOffset", - "widget.text.onMouseUp", -} - -var benchManyPaths = []string{ - "widget.window.name", - "widget.image.hOffset", - "widget.text.onMouseUp", - "widget.window.title", - "widget.image.alignment", - "widget.text.style", - "widget.window.height", - "widget.image.src", - "widget.text.data", - "widget.text.size", -} - -func BenchmarkGJSONGet(t *testing.B) { - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - for j := 0; j < len(benchPaths); j++ { - if Get(exampleJSON, benchPaths[j]).Type == Null { - t.Fatal("did not find the value") - } - } - } - t.N *= len(benchPaths) // because we are running against 3 paths -} -func BenchmarkGJSONGetMany4Paths(t *testing.B) { - benchmarkGJSONGetManyN(t, 4) -} -func BenchmarkGJSONGetMany8Paths(t *testing.B) { - benchmarkGJSONGetManyN(t, 8) -} -func BenchmarkGJSONGetMany16Paths(t *testing.B) { - benchmarkGJSONGetManyN(t, 16) -} -func BenchmarkGJSONGetMany32Paths(t *testing.B) { - benchmarkGJSONGetManyN(t, 32) -} -func BenchmarkGJSONGetMany64Paths(t *testing.B) { - benchmarkGJSONGetManyN(t, 64) -} -func BenchmarkGJSONGetMany128Paths(t *testing.B) { - benchmarkGJSONGetManyN(t, 128) -} -func benchmarkGJSONGetManyN(t *testing.B, n int) { - var paths []string - for len(paths) < n { - paths = append(paths, benchManyPaths...) - } - paths = paths[:n] - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - results := GetMany(exampleJSON, paths...) - if len(results) == 0 { - t.Fatal("did not find the value") - } - for j := 0; j < len(results); j++ { - if results[j].Type == Null { - t.Fatal("did not find the value") - } - } - } - t.N *= len(paths) // because we are running against 3 paths -} - -func BenchmarkGJSONUnmarshalMap(t *testing.B) { - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - for j := 0; j < len(benchPaths); j++ { - parts := strings.Split(benchPaths[j], ".") - m, _ := Parse(exampleJSON).Value().(map[string]interface{}) - var v interface{} - for len(parts) > 0 { - part := parts[0] - if len(parts) > 1 { - m = m[part].(map[string]interface{}) - if m == nil { - t.Fatal("did not find the value") - } - } else { - v = m[part] - if v == nil { - t.Fatal("did not find the value") - } - } - parts = parts[1:] - } - } - } - t.N *= len(benchPaths) // because we are running against 3 paths -} - -func BenchmarkGJSONUnmarshalStruct(t *testing.B) { - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - for j := 0; j < len(benchPaths); j++ { - var s BenchStruct - if err := Unmarshal([]byte(exampleJSON), &s); err != nil { - t.Fatal(err) - } - switch benchPaths[j] { - case "widget.window.name": - if s.Widget.Window.Name == "" { - t.Fatal("did not find the value") - } - case "widget.image.hOffset": - if s.Widget.Image.HOffset == 0 { - t.Fatal("did not find the value") - } - case "widget.text.onMouseUp": - if s.Widget.Text.OnMouseUp == "" { - t.Fatal("did not find the value") - } - } - } - } - t.N *= len(benchPaths) // because we are running against 3 paths -} - -func BenchmarkJSONUnmarshalMap(t *testing.B) { - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - for j := 0; j < len(benchPaths); j++ { - parts := strings.Split(benchPaths[j], ".") - var m map[string]interface{} - if err := json.Unmarshal([]byte(exampleJSON), &m); err != nil { - t.Fatal(err) - } - var v interface{} - for len(parts) > 0 { - part := parts[0] - if len(parts) > 1 { - m = m[part].(map[string]interface{}) - if m == nil { - t.Fatal("did not find the value") - } - } else { - v = m[part] - if v == nil { - t.Fatal("did not find the value") - } - } - parts = parts[1:] - } - } - } - t.N *= len(benchPaths) // because we are running against 3 paths -} - -func BenchmarkJSONUnmarshalStruct(t *testing.B) { - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - for j := 0; j < len(benchPaths); j++ { - var s BenchStruct - if err := json.Unmarshal([]byte(exampleJSON), &s); err != nil { - t.Fatal(err) - } - switch benchPaths[j] { - case "widget.window.name": - if s.Widget.Window.Name == "" { - t.Fatal("did not find the value") - } - case "widget.image.hOffset": - if s.Widget.Image.HOffset == 0 { - t.Fatal("did not find the value") - } - case "widget.text.onMouseUp": - if s.Widget.Text.OnMouseUp == "" { - t.Fatal("did not find the value") - } - } - } - } - t.N *= len(benchPaths) // because we are running against 3 paths -} - -func BenchmarkJSONDecoder(t *testing.B) { - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - for j := 0; j < len(benchPaths); j++ { - dec := json.NewDecoder(bytes.NewBuffer([]byte(exampleJSON))) - var found bool - outer: - for { - tok, err := dec.Token() - if err != nil { - if err == io.EOF { - break - } - t.Fatal(err) - } - switch v := tok.(type) { - case string: - if found { - // break out once we find the value. - break outer - } - switch benchPaths[j] { - case "widget.window.name": - if v == "name" { - found = true - } - case "widget.image.hOffset": - if v == "hOffset" { - found = true - } - case "widget.text.onMouseUp": - if v == "onMouseUp" { - found = true - } - } - } - } - if !found { - t.Fatal("field not found") - } - } - } - t.N *= len(benchPaths) // because we are running against 3 paths -} - -func BenchmarkFFJSONLexer(t *testing.B) { - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - for j := 0; j < len(benchPaths); j++ { - l := fflib.NewFFLexer([]byte(exampleJSON)) - var found bool - outer: - for { - t := l.Scan() - if t == fflib.FFTok_eof { - break - } - if t == fflib.FFTok_string { - b, _ := l.CaptureField(t) - v := string(b) - if found { - // break out once we find the value. - break outer - } - switch benchPaths[j] { - case "widget.window.name": - if v == "\"name\"" { - found = true - } - case "widget.image.hOffset": - if v == "\"hOffset\"" { - found = true - } - case "widget.text.onMouseUp": - if v == "\"onMouseUp\"" { - found = true - } - } - } - } - if !found { - t.Fatal("field not found") - } - } - } - t.N *= len(benchPaths) // because we are running against 3 paths -} - -func skipCC(l *jlexer.Lexer, n int) { - for i := 0; i < n; i++ { - l.Skip() - l.WantColon() - l.Skip() - l.WantComma() - } -} -func skipGroup(l *jlexer.Lexer, n int) { - l.WantColon() - l.Delim('{') - skipCC(l, n) - l.Delim('}') - l.WantComma() -} -func easyJSONWindowName(t *testing.B, l *jlexer.Lexer) { - if l.String() == "window" { - l.WantColon() - l.Delim('{') - skipCC(l, 1) - if l.String() == "name" { - l.WantColon() - if l.String() == "" { - t.Fatal("did not find the value") - } - } - } -} -func easyJSONImageHOffset(t *testing.B, l *jlexer.Lexer) { - if l.String() == "image" { - l.WantColon() - l.Delim('{') - skipCC(l, 1) - if l.String() == "hOffset" { - l.WantColon() - if l.Int() == 0 { - t.Fatal("did not find the value") - } - } - } -} -func easyJSONTextOnMouseUp(t *testing.B, l *jlexer.Lexer) { - if l.String() == "text" { - l.WantColon() - l.Delim('{') - skipCC(l, 5) - if l.String() == "onMouseUp" { - l.WantColon() - if l.String() == "" { - t.Fatal("did not find the value") - } - } - } -} -func easyJSONWidget(t *testing.B, l *jlexer.Lexer, j int) { - l.WantColon() - l.Delim('{') - switch benchPaths[j] { - case "widget.window.name": - skipCC(l, 1) - easyJSONWindowName(t, l) - case "widget.image.hOffset": - skipCC(l, 1) - if l.String() == "window" { - skipGroup(l, 4) - } - easyJSONImageHOffset(t, l) - case "widget.text.onMouseUp": - skipCC(l, 1) - if l.String() == "window" { - skipGroup(l, 4) - } - if l.String() == "image" { - skipGroup(l, 4) - } - easyJSONTextOnMouseUp(t, l) - } -} -func BenchmarkEasyJSONLexer(t *testing.B) { - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - for j := 0; j < len(benchPaths); j++ { - l := &jlexer.Lexer{Data: []byte(exampleJSON)} - l.Delim('{') - if l.String() == "widget" { - easyJSONWidget(t, l, j) - } - } - } - t.N *= len(benchPaths) // because we are running against 3 paths -} - -func BenchmarkJSONParserGet(t *testing.B) { - data := []byte(exampleJSON) - keys := make([][]string, 0, len(benchPaths)) - for i := 0; i < len(benchPaths); i++ { - keys = append(keys, strings.Split(benchPaths[i], ".")) - } - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - for j, k := range keys { - if j == 1 { - // "widget.image.hOffset" is a number - v, _ := jsonparser.GetInt(data, k...) - if v == 0 { - t.Fatal("did not find the value") - } - } else { - // "widget.window.name", - // "widget.text.onMouseUp", - v, _ := jsonparser.GetString(data, k...) - if v == "" { - t.Fatal("did not find the value") - } - } - } - } - t.N *= len(benchPaths) // because we are running against 3 paths -} -func jsoniterWindowName(t *testing.B, iter *jsoniter.Iterator) { - var v string - for { - key := iter.ReadObject() - if key != "window" { - iter.Skip() - continue - } - for { - key := iter.ReadObject() - if key != "name" { - iter.Skip() - continue - } - v = iter.ReadString() - break - } - break - } - if v == "" { - t.Fatal("did not find the value") - } -} - -func jsoniterTextOnMouseUp(t *testing.B, iter *jsoniter.Iterator) { - var v string - for { - key := iter.ReadObject() - if key != "text" { - iter.Skip() - continue - } - for { - key := iter.ReadObject() - if key != "onMouseUp" { - iter.Skip() - continue - } - v = iter.ReadString() - break - } - break - } - if v == "" { - t.Fatal("did not find the value") - } -} -func jsoniterImageOffset(t *testing.B, iter *jsoniter.Iterator) { - var v int - for { - key := iter.ReadObject() - if key != "image" { - iter.Skip() - continue - } - for { - key := iter.ReadObject() - if key != "hOffset" { - iter.Skip() - continue - } - v = iter.ReadInt() - break - } - break - } - if v == 0 { - t.Fatal("did not find the value") - } -} -func jsoniterWidget(t *testing.B, iter *jsoniter.Iterator, j int) { - for { - key := iter.ReadObject() - if key != "widget" { - iter.Skip() - continue - } - switch benchPaths[j] { - case "widget.window.name": - jsoniterWindowName(t, iter) - case "widget.image.hOffset": - jsoniterImageOffset(t, iter) - case "widget.text.onMouseUp": - jsoniterTextOnMouseUp(t, iter) - } - break - } -} - -func BenchmarkJSONIterator(t *testing.B) { - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - for j := 0; j < len(benchPaths); j++ { - iter := jsoniter.ParseString(exampleJSON) - jsoniterWidget(t, iter, j) - } - } - t.N *= len(benchPaths) // because we are running against 3 paths -} - -var massiveJSON = func() string { - var buf bytes.Buffer - buf.WriteString("[") - for i := 0; i < 100; i++ { - if i > 0 { - buf.WriteByte(',') - } - buf.WriteString(exampleJSON) - } - buf.WriteString("]") - return buf.String() -}() - -func BenchmarkConvertNone(t *testing.B) { - json := massiveJSON - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - Get(json, "50.widget.text.onMouseUp") - } -} -func BenchmarkConvertGet(t *testing.B) { - data := []byte(massiveJSON) - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - Get(string(data), "50.widget.text.onMouseUp") - } -} -func BenchmarkConvertGetBytes(t *testing.B) { - data := []byte(massiveJSON) - t.ReportAllocs() - t.ResetTimer() - for i := 0; i < t.N; i++ { - GetBytes(data, "50.widget.text.onMouseUp") - } -} -func BenchmarkParseUintNumParser(t *testing.B) { - var s = "634866135153775564" - for i := 0; i < t.N; i++ { - parseUint(s) - } -} -func BenchmarkStdlibParseUintNumParser(t *testing.B) { - var s = "634866135153775564" - for i := 0; i < t.N; i++ { - strconv.ParseUint(s, 10, 64) - } -} -func BenchmarkParseIntNumParser(t *testing.B) { - var s = "-634866135153775564" - for i := 0; i < t.N; i++ { - parseInt(s) - } -} -func BenchmarkStdlibParseIntNumParser(t *testing.B) { - var s = "-634866135153775564" - for i := 0; i < t.N; i++ { - strconv.ParseInt(s, 10, 64) - } -}