Created
July 18, 2020 10:08
-
-
Save itaysk/c9e470a6f419eaf6e27acd98bdb80f67 to your computer and use it in GitHub Desktop.
golang json bench
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// benchmark various json parsing and querying libraries on the use case of finding the image of the first container of a Kubernetes Pod | |
package jsonbench | |
import ( | |
"encoding/json" | |
"io/ioutil" | |
"testing" | |
"github.com/Jeffail/gabs/v2" | |
"github.com/itchyny/gojq" | |
jsoniter "github.com/json-iterator/go" | |
"github.com/stretchr/objx" | |
"github.com/tidwall/gjson" | |
"github.com/valyala/fastjson" | |
) | |
var jsonBytes []byte | |
func init() { | |
jsonBytes, _ = ioutil.ReadFile("./pod.json") | |
} | |
func BenchmarkEncodingJSONSafe(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
var parsed map[string]interface{} | |
_ = json.Unmarshal(jsonBytes, &parsed) | |
encodingJSONSafe(b, parsed) | |
} | |
} | |
func BenchmarkEncodingJSONSafeParsed(b *testing.B) { | |
var parsed map[string]interface{} | |
_ = json.Unmarshal(jsonBytes, &parsed) | |
for i := 0; i < b.N; i++ { | |
encodingJSONSafe(b, parsed) | |
} | |
} | |
func encodingJSONSafe(b *testing.B, parsed map[string]interface{}) { | |
specRaw, ok := parsed["spec"] | |
if !ok { | |
b.FailNow() | |
} | |
spec, ok := specRaw.(map[string]interface{}) | |
if !ok { | |
b.FailNow() | |
} | |
containersRaw, ok := spec["containers"] | |
if !ok { | |
b.FailNow() | |
} | |
containers, ok := containersRaw.([]interface{}) | |
if !ok { | |
b.FailNow() | |
} | |
if len(containers) == 0 { | |
b.FailNow() | |
} | |
cRaw := containers[0] | |
c, ok := cRaw.(map[string]interface{}) | |
if !ok { | |
b.FailNow() | |
} | |
imageRaw, ok := c["image"] | |
if !ok { | |
b.FailNow() | |
} | |
image, ok := imageRaw.(string) | |
if !ok { | |
b.FailNow() | |
} | |
if image != "nginx" { | |
b.Fatal(image) | |
} | |
} | |
func BenchmarkEncodingJSONUnsafe(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
var parsed map[string]interface{} | |
_ = json.Unmarshal(jsonBytes, &parsed) | |
encodingJSONUnsafe(b, parsed) | |
} | |
} | |
func BenchmarkEncodingJSONUnsafeParsed(b *testing.B) { | |
var parsed map[string]interface{} | |
_ = json.Unmarshal(jsonBytes, &parsed) | |
for i := 0; i < b.N; i++ { | |
encodingJSONUnsafe(b, parsed) | |
} | |
} | |
func encodingJSONUnsafe(b *testing.B, parsed map[string]interface{}) { | |
res := parsed["spec"].(map[string]interface{})["containers"].([]interface{})[0].(map[string]interface{})["image"] | |
if res.(string) != "nginx" { | |
b.Fatal(res) | |
} | |
} | |
func BenchmarkGabsPath(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
parsed, _ := gabs.ParseJSON(jsonBytes) | |
gabsPath(b, parsed) | |
} | |
} | |
func BenchmarkGabsPathParsed(b *testing.B) { | |
parsed, _ := gabs.ParseJSON(jsonBytes) | |
for i := 0; i < b.N; i++ { | |
gabsPath(b, parsed) | |
} | |
} | |
func gabsPath(b *testing.B, parsed *gabs.Container) { | |
path := "spec.containers.0.image" | |
res := parsed.Path(path) | |
if !res.Exists() { | |
b.Fatal() | |
} | |
if s, ok := res.Data().(string); ok && s != "nginx" { | |
b.Fatal(res) | |
} | |
} | |
func BenchmarkGabsJSONPointer(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
parsed, _ := gabs.ParseJSON(jsonBytes) | |
gabsJSONPointer(b, parsed) | |
} | |
} | |
func BenchmarkGabsJSONPointerParsed(b *testing.B) { | |
parsed, _ := gabs.ParseJSON(jsonBytes) | |
for i := 0; i < b.N; i++ { | |
gabsJSONPointer(b, parsed) | |
} | |
} | |
func gabsJSONPointer(b *testing.B, parsed *gabs.Container) { | |
path := "/spec/containers/0/image" | |
res, err := parsed.JSONPointer(path) | |
if err != nil { | |
b.Fatal(err) | |
} | |
if s, ok := res.Data().(string); ok && s != "nginx" { | |
b.Fatal(res) | |
} | |
} | |
func BenchmarkGJSONGet(b *testing.B) { | |
path := "spec.containers.0.image" | |
for i := 0; i < b.N; i++ { | |
res := gjson.GetBytes(jsonBytes, path) | |
if !res.Exists() { | |
b.Fatal() | |
} | |
if res.String() != "nginx" { | |
b.Fatal(res) | |
} | |
} | |
} | |
func BenchmarkGJSONParsed(b *testing.B) { | |
path := "spec.containers.0.image" | |
parsed := gjson.ParseBytes(jsonBytes) | |
for i := 0; i < b.N; i++ { | |
res := parsed.Get(path) | |
if !res.Exists() { | |
b.Fatal() | |
} | |
if res.String() != "nginx" { | |
b.Fatal(res) | |
} | |
} | |
} | |
func BenchmarkJSONIterGet(b *testing.B) { | |
path := []interface{}{"spec", "containers", 0, "image"} | |
for i := 0; i < b.N; i++ { | |
res := jsoniter.Get(jsonBytes, path...) | |
if res.LastError() != nil { | |
b.Fatal() | |
} | |
if res.ToString() != "nginx" { | |
b.Fatal(res) | |
} | |
} | |
} | |
func BenchmarkObjX(b *testing.B) { | |
for i := 0; i < b.N; i++ { | |
parsed, _ := objx.FromJSON(string(jsonBytes)) | |
benchObjx(b, parsed) | |
} | |
} | |
func BenchmarkObjXParsed(b *testing.B) { | |
parsed, _ := objx.FromJSON(string(jsonBytes)) | |
for i := 0; i < b.N; i++ { | |
benchObjx(b, parsed) | |
} | |
} | |
func benchObjx(b *testing.B, parsed objx.Map) { | |
path := "spec.containers[0].image" | |
res := parsed.Get(path) | |
if !res.IsStr() { | |
b.Fatal() | |
} | |
if res.Str() != "nginx" { | |
b.Fatal(res) | |
} | |
} | |
func BenchmarkFastJSONGet(b *testing.B) { | |
path := []string{"spec", "containers", "0", "image"} | |
for i := 0; i < b.N; i++ { | |
res := fastjson.GetString(jsonBytes, path...) | |
if res != "nginx" { | |
b.Fatal(res) | |
} | |
} | |
} | |
func BenchmarkFastJSONParsed(b *testing.B) { | |
path := []string{"spec", "containers", "0", "image"} | |
parsed := fastjson.MustParseBytes(jsonBytes) | |
for i := 0; i < b.N; i++ { | |
res := parsed.GetStringBytes(path...) | |
if string(res) != "nginx" { | |
b.Fatal(res) | |
} | |
} | |
} | |
// too slow to consider | |
func SkipBenchmarkJQParsed(b *testing.B) { | |
path := ".spec.containers[0].image" | |
var j map[string]interface{} | |
json.Unmarshal(jsonBytes, &j) | |
q, _ := gojq.Parse(path) | |
for i := 0; i < b.N; i++ { | |
iter := q.Run(j) | |
for { | |
v, more := iter.Next() | |
if !more { | |
break | |
} | |
if err, ok := v.(error); ok { | |
b.Fatal(err) | |
} | |
if s, ok := v.(string); ok { | |
if s != "nginx" { | |
b.Fatal(s) | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment