Skip to content

Instantly share code, notes, and snippets.

@FZambia
Created November 11, 2016 17:05
Show Gist options
  • Select an option

  • Save FZambia/e0026ecc2fc7d7a4d62015e7bc7252c4 to your computer and use it in GitHub Desktop.

Select an option

Save FZambia/e0026ecc2fc7d7a4d62015e7bc7252c4 to your computer and use it in GitHub Desktop.
Jsonparser GJSON Benchmarks
package apiv1
import (
"encoding/json"
"testing"
"github.com/buger/jsonparser"
"github.com/centrifugal/centrifugo/libcentrifugo/proto"
"github.com/centrifugal/centrifugo/libcentrifugo/raw"
"github.com/tidwall/gjson"
)
var (
arrayJSONPrefix byte = '['
objectJSONPrefix byte = '{'
)
func apiCommandsFromJSON(msg []byte) ([]proto.ApiCommand, error) {
var cmds []proto.ApiCommand
if len(msg) == 0 {
return cmds, nil
}
firstByte := msg[0]
switch firstByte {
case objectJSONPrefix:
// single command request
var command proto.ApiCommand
err := json.Unmarshal(msg, &command)
if err != nil {
return nil, err
}
cmds = append(cmds, command)
case arrayJSONPrefix:
// array of commands received
err := json.Unmarshal(msg, &cmds)
if err != nil {
return nil, err
}
default:
return nil, proto.ErrInvalidMessage
}
return cmds, nil
}
func apiCommandsFromGJSON(msg []byte) ([]proto.ApiCommand, error) {
if len(msg) == 0 {
return nil, nil
}
firstByte := msg[0]
switch firstByte {
case objectJSONPrefix:
var cmds = make([]proto.ApiCommand, 1)
var cmd proto.ApiCommand
cmd.Method = gjson.GetBytes(msg, "method").String()
cmd.Params = raw.Raw(gjson.GetBytes(msg, "params").Raw)
cmds[0] = cmd
return cmds, nil
case arrayJSONPrefix:
res := gjson.Parse(string(msg))
cs := res.Array()
var cmds = make([]proto.ApiCommand, len(cs))
for i, r := range cs {
var cmd proto.ApiCommand
cmd.Method = r.Get("method").String()
cmd.Params = raw.Raw(r.Get("params").Raw)
cmds[i] = cmd
}
return cmds, nil
default:
return nil, proto.ErrInvalidMessage
}
}
var testJSONObject = `{"method": "publish", "params": {"channel": "channel", "data": "hello"}}`
var testJSONArraySingle = `[
{"method": "publish", "params": {"channel": "channel", "data": "hello"}}
]`
var testJSONArrayMany = `[
{"method": "publish", "params": {"channel": "channel", "data": "hello"}},
{"method": "publish", "params": {"channel": "channel", "data": "hello"}},
{"method": "publish", "params": {"channel": "channel", "data": "hello"}},
{"method": "publish", "params": {"channel": "channel", "data": "hello"}},
{"method": "publish", "params": {"channel": "channel", "data": "hello"}},
{"method": "publish", "params": {"channel": "channel", "data": "hello"}}
]`
func BenchmarkJSONObject(b *testing.B) {
msg := []byte(testJSONObject)
b.ResetTimer()
for i := 0; i < b.N; i++ {
commands, err := apiCommandsFromJSON(msg)
if err != nil {
panic(err)
}
for _, command := range commands {
var cmd proto.PublishAPICommand
err = json.Unmarshal(command.Params, &cmd)
if err != nil {
panic(err)
}
if cmd.Channel != "channel" {
panic("wrong channel")
}
}
}
}
func BenchmarkJSONObjectGJSON(b *testing.B) {
msg := []byte(testJSONObject)
b.ResetTimer()
for i := 0; i < b.N; i++ {
commands, err := apiCommandsFromGJSON(msg)
if err != nil {
panic(err)
}
for _, command := range commands {
handlePublish(&command)
}
}
}
func BenchmarkJSONObjectGJSONFast(b *testing.B) {
msg := []byte(testJSONObject)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if len(msg) == 0 {
panic(1)
}
firstByte := msg[0]
switch firstByte {
case objectJSONPrefix:
var cmd proto.ApiCommand
cmd.Method = gjson.GetBytes(msg, "method").String()
cmd.Params = raw.Raw(gjson.GetBytes(msg, "params").Raw)
handlePublish(&cmd)
case arrayJSONPrefix:
res := gjson.Parse(string(msg))
cs := res.Array()
for _, r := range cs {
var cmd proto.ApiCommand
cmd.Method = r.Get("method").String()
cmd.Params = raw.Raw(r.Get("params").Raw)
handlePublish(&cmd)
}
default:
}
}
}
func BenchmarkJSONObjectJsonparser(b *testing.B) {
msg := []byte(testJSONObject)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if len(msg) == 0 {
panic(1)
}
firstByte := msg[0]
switch firstByte {
case objectJSONPrefix:
var cmd proto.ApiCommand
jsonparser.EachKey(msg, func(idx int, val []byte, vt jsonparser.ValueType, err error) {
switch idx {
case 0:
cmd.UID = string(val)
case 1:
cmd.Method = string(val)
case 2:
cmd.Params = raw.Raw(val)
}
}, paths...)
handlePublishJsonparser(&cmd)
case arrayJSONPrefix:
jsonparser.ArrayEach(msg, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
var cmd proto.ApiCommand
jsonparser.EachKey(value, func(idx int, val []byte, vt jsonparser.ValueType, err error) {
switch idx {
case 0:
cmd.UID = string(val)
case 1:
cmd.Method = string(val)
case 2:
cmd.Params = raw.Raw(val)
}
}, paths...)
handlePublishJsonparser(&cmd)
})
default:
}
}
}
func BenchmarkJSONArraySingle(b *testing.B) {
msg := []byte(testJSONArraySingle)
b.ResetTimer()
for i := 0; i < b.N; i++ {
commands, err := apiCommandsFromJSON(msg)
if err != nil {
panic(err)
}
for _, command := range commands {
var cmd proto.PublishAPICommand
err = json.Unmarshal(command.Params, &cmd)
if err != nil {
panic(err)
}
if cmd.Channel != "channel" {
panic("wrong channel")
}
}
}
}
func BenchmarkJSONArraySingleGJSON(b *testing.B) {
msg := []byte(testJSONArraySingle)
b.ResetTimer()
for i := 0; i < b.N; i++ {
commands, err := apiCommandsFromGJSON(msg)
if err != nil {
panic(err)
}
for _, command := range commands {
handlePublish(&command)
}
}
}
func BenchmarkJSONArraySingleGJSONFast(b *testing.B) {
msg := []byte(testJSONArraySingle)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if len(msg) == 0 {
panic(1)
}
firstByte := msg[0]
switch firstByte {
case objectJSONPrefix:
var cmd proto.ApiCommand
cmd.Method = gjson.GetBytes(msg, "method").String()
cmd.Params = raw.Raw(gjson.GetBytes(msg, "params").Raw)
handlePublish(&cmd)
case arrayJSONPrefix:
res := gjson.Parse(string(msg))
cs := res.Array()
for _, r := range cs {
var cmd proto.ApiCommand
cmd.Method = r.Get("method").String()
cmd.Params = raw.Raw(r.Get("params").Raw)
handlePublish(&cmd)
}
default:
}
}
}
func BenchmarkJSONArraySingleJsonparser(b *testing.B) {
msg := []byte(testJSONArraySingle)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if len(msg) == 0 {
panic(1)
}
firstByte := msg[0]
switch firstByte {
case objectJSONPrefix:
var cmd proto.ApiCommand
jsonparser.EachKey(msg, func(idx int, val []byte, vt jsonparser.ValueType, err error) {
switch idx {
case 0:
cmd.UID = string(val)
case 1:
cmd.Method = string(val)
case 2:
cmd.Params = raw.Raw(val)
}
}, paths...)
handlePublishJsonparser(&cmd)
case arrayJSONPrefix:
jsonparser.ArrayEach(msg, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
var cmd proto.ApiCommand
jsonparser.EachKey(value, func(idx int, val []byte, vt jsonparser.ValueType, err error) {
switch idx {
case 0:
cmd.UID = string(val)
case 1:
cmd.Method = string(val)
case 2:
cmd.Params = raw.Raw(val)
}
}, paths...)
handlePublishJsonparser(&cmd)
})
default:
}
}
}
func BenchmarkJSONArrayMany(b *testing.B) {
msg := []byte(testJSONArrayMany)
b.ResetTimer()
for i := 0; i < b.N; i++ {
commands, err := apiCommandsFromJSON(msg)
if err != nil {
panic(err)
}
for _, command := range commands {
var cmd proto.PublishAPICommand
err = json.Unmarshal(command.Params, &cmd)
if err != nil {
panic(err)
}
if cmd.Channel != "channel" {
panic("wrong channel")
}
}
}
}
func BenchmarkJSONArrayManyGJSON(b *testing.B) {
msg := []byte(testJSONArrayMany)
b.ResetTimer()
for i := 0; i < b.N; i++ {
commands, err := apiCommandsFromGJSON(msg)
if err != nil {
panic(err)
}
for _, command := range commands {
handlePublish(&command)
}
}
}
func handlePublish(command *proto.ApiCommand) error {
var cmd proto.PublishAPICommand
params := string(command.Params)
cmd.Channel = gjson.Get(params, "channel").String()
cmd.Data = raw.Raw(gjson.Get(params, "data").Raw)
cmd.Client = gjson.Get(params, "client").String()
if cmd.Channel != "channel" {
panic("wrong channel")
}
return nil
}
func handlePublishJsonparser(command *proto.ApiCommand) error {
var cmd proto.PublishAPICommand
jsonparser.EachKey(command.Params, func(idx int, val []byte, vt jsonparser.ValueType, err error) {
switch idx {
case 0:
cmd.Channel = string(val)
case 1:
cmd.Data = raw.Raw(val)
case 2:
cmd.Client = string(val)
}
}, publishPaths...)
if cmd.Channel != "channel" {
panic("wrong channel")
}
return nil
}
func BenchmarkJSONArrayManyGJSONFast(b *testing.B) {
msg := []byte(testJSONArrayMany)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if len(msg) == 0 {
panic(1)
}
firstByte := msg[0]
switch firstByte {
case objectJSONPrefix:
var cmd proto.ApiCommand
cmd.Method = gjson.GetBytes(msg, "method").String()
cmd.Params = raw.Raw(gjson.GetBytes(msg, "params").Raw)
handlePublish(&cmd)
case arrayJSONPrefix:
res := gjson.Parse(string(msg))
cs := res.Array()
for _, r := range cs {
var cmd proto.ApiCommand
cmd.Method = r.Get("method").String()
cmd.Params = raw.Raw(r.Get("params").Raw)
handlePublish(&cmd)
}
default:
}
}
}
var paths = [][]string{
[]string{"uid"},
[]string{"method"},
[]string{"params"},
}
var publishPaths = [][]string{
[]string{"channel"},
[]string{"data"},
[]string{"client"},
}
func BenchmarkJSONArrayManyJsonparser(b *testing.B) {
msg := []byte(testJSONArrayMany)
b.ResetTimer()
for i := 0; i < b.N; i++ {
if len(msg) == 0 {
panic(1)
}
firstByte := msg[0]
switch firstByte {
case objectJSONPrefix:
var cmd proto.ApiCommand
jsonparser.EachKey(msg, func(idx int, val []byte, vt jsonparser.ValueType, err error) {
switch idx {
case 0:
cmd.UID = string(val)
case 1:
cmd.Method = string(val)
case 2:
cmd.Params = raw.Raw(val)
}
}, paths...)
handlePublishJsonparser(&cmd)
case arrayJSONPrefix:
jsonparser.ArrayEach(msg, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
var cmd proto.ApiCommand
jsonparser.EachKey(value, func(idx int, val []byte, vt jsonparser.ValueType, err error) {
switch idx {
case 0:
cmd.UID = string(val)
case 1:
cmd.Method = string(val)
case 2:
cmd.Params = raw.Raw(val)
}
}, paths...)
handlePublishJsonparser(&cmd)
})
default:
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment