Skip to content

Instantly share code, notes, and snippets.

@urso

urso/Results Secret

Created Jun 14, 2016
Embed
What would you like to do?
golang json encoder benchmark
package enc
import (
"bytes"
"encoding/json"
"io"
"math/rand"
"reflect"
"strings"
"testing"
"github.com/pquerna/ffjson/ffjson"
"github.com/ugorji/go/codec"
)
type marshallerFactory func(io.Writer) func(interface{}) error
type MapStr map[string]interface{}
// Codec MapStr conversion extension
type mapStrExt struct{}
var (
arrBool []bool
arrInt []int
arrUint8 []uint8
arrFloat32 []float32
arrString []string
mBool = map[string]bool{}
mInt = map[string]int{}
mUint8 = map[string]uint8{}
mFloat32 = map[string]float32{}
mString = map[string]string{}
msBool = MapStr{}
msInt = MapStr{}
msUint8 = MapStr{}
msFloat32 = MapStr{}
msString = MapStr{}
)
func init() {
L := 100
arrBool = make([]bool, L)
arrInt = make([]int, L)
arrUint8 = make([]uint8, L)
arrFloat32 = make([]float32, L)
arrString = make([]string, L)
testStrings := strings.Fields("The quick brown fox jumps over the lazy dog")
for i := 0; i < L; i++ {
arrBool[i] = rand.Intn(2) != 0
arrInt[i] = rand.Int()
arrUint8[i] = uint8(rand.Uint32())
arrFloat32[i] = rand.Float32()
arrString[i] = testStrings[rand.Intn(len(testStrings)-1)]
}
for _, key := range testStrings {
mBool[key] = rand.Intn(2) != 0
mInt[key] = rand.Int()
mUint8[key] = uint8(rand.Uint32())
mFloat32[key] = rand.Float32()
mString[key] = testStrings[rand.Intn(len(testStrings)-1)]
msBool[key] = rand.Intn(2) != 0
msInt[key] = rand.Int()
msUint8[key] = uint8(rand.Uint32())
msFloat32[key] = rand.Float32()
msString[key] = testStrings[rand.Intn(len(testStrings)-1)]
}
registerConvertible(
reflect.TypeOf(MapStr{}), reflect.TypeOf(map[string]interface{}{}))
}
func benchEncodePrimitives(b *testing.B, f marshallerFactory) {
primitives := []interface{}{
int(1),
true,
false,
nil,
uint8(10),
float32(3.14),
"testing",
}
benchRun(b, primitives, f)
}
func benchEncodeArray(b *testing.B, f marshallerFactory) {
arrays := []interface{}{
arrInt,
arrBool,
arrUint8,
arrFloat32,
arrString,
}
arrays = append(arrays, arrays)
benchRun(b, arrays, f)
}
func benchEncodeMap(b *testing.B, f marshallerFactory) {
maps := []interface{}{
mInt,
mBool,
mUint8,
mFloat32,
mString,
}
maps = append(maps, map[string]interface{}{
"all": maps,
})
benchRun(b, maps, f)
}
func benchEncodeMapStr(b *testing.B, f marshallerFactory) {
maps := []interface{}{
msInt,
msBool,
msUint8,
msFloat32,
msString,
}
maps = append(maps, MapStr{"all": maps})
benchRun(b, maps, f)
}
func benchRun(b *testing.B, content []interface{}, f marshallerFactory) {
var tmp [4096]byte
buf := bytes.NewBuffer(tmp[:0])
enc := f(buf)
for i := 0; i < b.N; i++ {
for _, c := range content {
buf.Reset()
err := enc(c)
if err != nil {
b.Fatal(err)
}
}
}
}
func BenchmarkPrimitivesJSONMarshal(b *testing.B) {
benchEncodePrimitives(b, makeJSONMarshal)
}
func BenchmarkPrimitivesJSONEncoder(b *testing.B) {
benchEncodePrimitives(b, makeJSONEncoder)
}
func BenchmarkPrimitivesFFJSON(b *testing.B) {
benchEncodePrimitives(b, makeFFJSONEncoder)
}
func BenchmarkPrimitivesCodecJSON(b *testing.B) {
benchEncodePrimitives(b, makeCodecJSONEncoder)
}
func BenchmarkPrimitivesCodecMP(b *testing.B) {
benchEncodePrimitives(b, makeCodecMSGPACKEncoder)
}
func BenchmarkPrimitivesJSONEnc(b *testing.B) {
benchEncodePrimitives(b, makeJSONEnc)
}
func BenchmarkArrayJSONMarshal(b *testing.B) {
benchEncodeArray(b, makeJSONMarshal)
}
func BenchmarkArrayJSONEncoder(b *testing.B) {
benchEncodeArray(b, makeJSONEncoder)
}
func BenchmarkArrayFFJSON(b *testing.B) {
benchEncodeArray(b, makeFFJSONEncoder)
}
func BenchmarkArrayCodecJSON(b *testing.B) {
benchEncodeArray(b, makeCodecJSONEncoder)
}
func BenchmarkArrayCodecMP(b *testing.B) {
benchEncodeArray(b, makeCodecMSGPACKEncoder)
}
func BenchmarkArrayJSONEnc(b *testing.B) {
benchEncodeArray(b, makeJSONEnc)
}
func BenchmarkMapJSONMarshal(b *testing.B) {
benchEncodeMap(b, makeJSONMarshal)
}
func BenchmarkMapJSONEncoder(b *testing.B) {
benchEncodeMap(b, makeJSONEncoder)
}
func BenchmarkMapFFJSON(b *testing.B) {
benchEncodeMap(b, makeFFJSONEncoder)
}
func BenchmarkMapCodecJSON(b *testing.B) {
benchEncodeMap(b, makeCodecJSONEncoder)
}
func BenchmarkMapCodecMP(b *testing.B) {
benchEncodeMap(b, makeCodecMSGPACKEncoder)
}
func BenchmarkMapJSONEnc(b *testing.B) {
benchEncodeMap(b, makeJSONEnc)
}
func BenchmarkMapStrJSONMarshal(b *testing.B) {
benchEncodeMapStr(b, makeJSONMarshal)
}
func BenchmarkMapStrJSONEncoder(b *testing.B) {
benchEncodeMapStr(b, makeJSONEncoder)
}
func BenchmarkMapStrFFJSON(b *testing.B) {
benchEncodeMapStr(b, makeFFJSONEncoder)
}
func BenchmarkMapStrCodecJSON(b *testing.B) {
benchEncodeMapStr(b, makeCodecJSONEncoder)
}
func BenchmarkMapStrCodecJSONExt(b *testing.B) {
benchEncodeMapStr(b, makeCodecJSONEncoderExt)
}
func BenchmarkMapStrCodecMP(b *testing.B) {
benchEncodeMapStr(b, makeCodecMSGPACKEncoder)
}
func BenchmarkMapStrJSONEnc(b *testing.B) {
benchEncodeMapStr(b, makeJSONEnc)
}
func makeJSONMarshal(out io.Writer) func(interface{}) error {
return func(v interface{}) error {
bytes, err := json.Marshal(v)
out.Write(bytes)
return err
}
}
func makeJSONEncoder(out io.Writer) func(interface{}) error {
return json.NewEncoder(out).Encode
}
func makeFFJSONEncoder(out io.Writer) func(interface{}) error {
enc := ffjson.NewEncoder(out)
return enc.Encode
}
func makeJSONEnc(out io.Writer) func(interface{}) error {
enc := &bufferedJSONEncoder{}
enc.bufferedEncoder.buf = bytes.NewBuffer(nil)
enc.jsonEncoder.out = enc.bufferedEncoder.buf
enc.self = enc
// enc.self = Visitor{&enc.jsonEncoder}
return func(v interface{}) error {
enc.Reset()
err := enc.Encode(v)
out.Write(enc.buf.Bytes())
return err
}
}
func makeCodecJSONEncoder(out io.Writer) func(interface{}) error {
var jh codec.JsonHandle
enc := codec.NewEncoder(out, &jh)
return enc.Encode
}
func makeCodecJSONEncoderExt(out io.Writer) func(interface{}) error {
var jh codec.JsonHandle
jh.SetInterfaceExt(reflect.TypeOf(MapStr{}), 1, mapStrExt{})
enc := codec.NewEncoder(out, &jh)
return enc.Encode
}
func makeCodecMSGPACKEncoder(out io.Writer) func(interface{}) error {
var mh codec.MsgpackHandle
enc := codec.NewEncoder(out, &mh)
return enc.Encode
}
func (mapStrExt) ConvertExt(v interface{}) interface{} {
return map[string]interface{}(v.(MapStr))
}
func (mapStrExt) UpdateExt(dst interface{}, src interface{}) {
}
JSONEnc = experimental golang primitives-only json encoder. See gist: https://gist.github.com/urso/e0bbea1f4e572ed9c8162c415faf3dce
(similar to: CodecJSON fast-path for primitives + support for aliases + custom backend possible, e.g. register alias for common.MapStr)
JSONMarshal = json.Marshal per doc
JSONEncoder = json.NewEncoder (re-use encoder) per test
FFJSON = https://github.com/pquerna/ffjson
CodecJSON = go/codec JSON encoder: https://github.com/ugorji/go/tree/master/codec
CodecJSONExt = like CodecJSON with registered type conversion for common.MapStr->map[string]interface{}
(can not be re-used for other codecs as is)
CodecMP = go/codec MessagePack encoder: https://github.com/ugorji/go/tree/master/codec
# primitive types (int, boo, string) only
BenchmarkPrimitivesJSONMarshal-4 500000 2611 ns/op 1280 B/op 13 allocs/op
BenchmarkPrimitivesJSONEncoder-4 500000 2156 ns/op 48 B/op 6 allocs/op
BenchmarkPrimitivesFFJSON-4 500000 2322 ns/op 48 B/op 6 allocs/op
BenchmarkPrimitivesCodecJSON-4 1000000 1325 ns/op 0 B/op 0 allocs/op
BenchmarkPrimitivesCodecMP-4 1000000 1060 ns/op 0 B/op 0 allocs/op
BenchmarkPrimitivesJSONEnc-4 2000000 689 ns/op 0 B/op 0 allocs/op
# array of primitive types + array of array
BenchmarkArrayJSONMarshal-4 20000 92830 ns/op 25353 B/op 43 allocs/op
BenchmarkArrayJSONEncoder-4 20000 82301 ns/op 377 B/op 13 allocs/op
BenchmarkArrayFFJSON-4 20000 82264 ns/op 377 B/op 13 allocs/op
BenchmarkArrayCodecJSON-4 20000 78559 ns/op 0 B/op 0 allocs/op
BenchmarkArrayCodecMP-4 50000 30907 ns/op 0 B/op 0 allocs/op
BenchmarkArrayJSONEnc-4 20000 76381 ns/op 2 B/op 0 allocs/op
# go maps + nesting: map[string]T and map[string]map[string]T
BenchmarkMapJSONMarshal-4 30000 48879 ns/op 10640 B/op 245 allocs/op
BenchmarkMapJSONEncoder-4 30000 45542 ns/op 5793 B/op 227 allocs/op
BenchmarkMapFFJSON-4 30000 45792 ns/op 5793 B/op 227 allocs/op
BenchmarkMapCodecJSON-4 100000 18159 ns/op 0 B/op 0 allocs/op
BenchmarkMapCodecMP-4 200000 9746 ns/op 0 B/op 0 allocs/op
BenchmarkMapJSONEnc-4 100000 14584 ns/op 0 B/op 0 allocs/op
# go map[string]interface{} aliased as common.MapStr
BenchmarkMapStrJSONMarshal-4 20000 61118 ns/op 11968 B/op 334 allocs/op
BenchmarkMapStrJSONEncoder-4 30000 56454 ns/op 7409 B/op 317 allocs/op
BenchmarkMapStrFFJSON-4 30000 57617 ns/op 7409 B/op 317 allocs/op
BenchmarkMapStrCodecJSON-4 30000 44862 ns/op 6240 B/op 204 allocs/op
BenchmarkMapStrCodecJSONExt-4 100000 22846 ns/op 0 B/op 0 allocs/op
BenchmarkMapStrCodecMP-4 50000 37067 ns/op 6240 B/op 204 allocs/op
BenchmarkMapStrJSONEnc-4 100000 19138 ns/op 0 B/op 0 allocs/op
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment