Skip to content

Instantly share code, notes, and snippets.

@itaysk
Created July 18, 2020 10:08
Show Gist options
  • Save itaysk/c9e470a6f419eaf6e27acd98bdb80f67 to your computer and use it in GitHub Desktop.
Save itaysk/c9e470a6f419eaf6e27acd98bdb80f67 to your computer and use it in GitHub Desktop.
golang json bench
// 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