forked from mirror/gjson
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
This commit is contained in:
parent
c784c41781
commit
6daf3373dc
|
@ -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)
|
||||
|
|
582
gjson_test.go
582
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue