Try to use ffjson with custom encoder, but it's even slower?!

Stop/start benchmark around code that massages vecs into custom structs
for encoding.
This commit is contained in:
Bill Thiede 2019-11-19 21:48:27 -08:00
parent 68088ceb3f
commit 05b8898d9f
4 changed files with 357 additions and 9 deletions

3
go.sum
View File

@ -15,6 +15,7 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
@ -69,6 +70,7 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 h1:xoIK0ctDddBMnc74udxJYBqlo9Ylnsp1waqjLsnef20=
github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M=
@ -104,6 +106,7 @@ github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:Udh
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=

7
src/ffjsonvec3/vec3.go Normal file
View File

@ -0,0 +1,7 @@
package ffjsonvec3
type Vec3 struct {
X float32 `json:"x"`
Y float32 `json:"y"`
Z float32 `json:"z"`
}

View File

@ -0,0 +1,315 @@
// Code generated by ffjson <https://github.com/pquerna/ffjson>. DO NOT EDIT.
// source: vec3.go
package ffjsonvec3
import (
"bytes"
"fmt"
fflib "github.com/pquerna/ffjson/fflib/v1"
)
// MarshalJSON marshal bytes to json - template
func (j *Vec3) MarshalJSON() ([]byte, error) {
var buf fflib.Buffer
if j == nil {
buf.WriteString("null")
return buf.Bytes(), nil
}
err := j.MarshalJSONBuf(&buf)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// MarshalJSONBuf marshal buff to json - template
func (j *Vec3) MarshalJSONBuf(buf fflib.EncodingBuffer) error {
if j == nil {
buf.WriteString("null")
return nil
}
var err error
var obj []byte
_ = obj
_ = err
buf.WriteString(`{"x":`)
fflib.AppendFloat(buf, float64(j.X), 'g', -1, 32)
buf.WriteString(`,"y":`)
fflib.AppendFloat(buf, float64(j.Y), 'g', -1, 32)
buf.WriteString(`,"z":`)
fflib.AppendFloat(buf, float64(j.Z), 'g', -1, 32)
buf.WriteByte('}')
return nil
}
const (
ffjtVec3base = iota
ffjtVec3nosuchkey
ffjtVec3X
ffjtVec3Y
ffjtVec3Z
)
var ffjKeyVec3X = []byte("x")
var ffjKeyVec3Y = []byte("y")
var ffjKeyVec3Z = []byte("z")
// UnmarshalJSON umarshall json - template of ffjson
func (j *Vec3) UnmarshalJSON(input []byte) error {
fs := fflib.NewFFLexer(input)
return j.UnmarshalJSONFFLexer(fs, fflib.FFParse_map_start)
}
// UnmarshalJSONFFLexer fast json unmarshall - template ffjson
func (j *Vec3) UnmarshalJSONFFLexer(fs *fflib.FFLexer, state fflib.FFParseState) error {
var err error
currentKey := ffjtVec3base
_ = currentKey
tok := fflib.FFTok_init
wantedTok := fflib.FFTok_init
mainparse:
for {
tok = fs.Scan()
// println(fmt.Sprintf("debug: tok: %v state: %v", tok, state))
if tok == fflib.FFTok_error {
goto tokerror
}
switch state {
case fflib.FFParse_map_start:
if tok != fflib.FFTok_left_bracket {
wantedTok = fflib.FFTok_left_bracket
goto wrongtokenerror
}
state = fflib.FFParse_want_key
continue
case fflib.FFParse_after_value:
if tok == fflib.FFTok_comma {
state = fflib.FFParse_want_key
} else if tok == fflib.FFTok_right_bracket {
goto done
} else {
wantedTok = fflib.FFTok_comma
goto wrongtokenerror
}
case fflib.FFParse_want_key:
// json {} ended. goto exit. woo.
if tok == fflib.FFTok_right_bracket {
goto done
}
if tok != fflib.FFTok_string {
wantedTok = fflib.FFTok_string
goto wrongtokenerror
}
kn := fs.Output.Bytes()
if len(kn) <= 0 {
// "" case. hrm.
currentKey = ffjtVec3nosuchkey
state = fflib.FFParse_want_colon
goto mainparse
} else {
switch kn[0] {
case 'x':
if bytes.Equal(ffjKeyVec3X, kn) {
currentKey = ffjtVec3X
state = fflib.FFParse_want_colon
goto mainparse
}
case 'y':
if bytes.Equal(ffjKeyVec3Y, kn) {
currentKey = ffjtVec3Y
state = fflib.FFParse_want_colon
goto mainparse
}
case 'z':
if bytes.Equal(ffjKeyVec3Z, kn) {
currentKey = ffjtVec3Z
state = fflib.FFParse_want_colon
goto mainparse
}
}
if fflib.SimpleLetterEqualFold(ffjKeyVec3Z, kn) {
currentKey = ffjtVec3Z
state = fflib.FFParse_want_colon
goto mainparse
}
if fflib.SimpleLetterEqualFold(ffjKeyVec3Y, kn) {
currentKey = ffjtVec3Y
state = fflib.FFParse_want_colon
goto mainparse
}
if fflib.SimpleLetterEqualFold(ffjKeyVec3X, kn) {
currentKey = ffjtVec3X
state = fflib.FFParse_want_colon
goto mainparse
}
currentKey = ffjtVec3nosuchkey
state = fflib.FFParse_want_colon
goto mainparse
}
case fflib.FFParse_want_colon:
if tok != fflib.FFTok_colon {
wantedTok = fflib.FFTok_colon
goto wrongtokenerror
}
state = fflib.FFParse_want_value
continue
case fflib.FFParse_want_value:
if tok == fflib.FFTok_left_brace || tok == fflib.FFTok_left_bracket || tok == fflib.FFTok_integer || tok == fflib.FFTok_double || tok == fflib.FFTok_string || tok == fflib.FFTok_bool || tok == fflib.FFTok_null {
switch currentKey {
case ffjtVec3X:
goto handle_X
case ffjtVec3Y:
goto handle_Y
case ffjtVec3Z:
goto handle_Z
case ffjtVec3nosuchkey:
err = fs.SkipField(tok)
if err != nil {
return fs.WrapErr(err)
}
state = fflib.FFParse_after_value
goto mainparse
}
} else {
goto wantedvalue
}
}
}
handle_X:
/* handler: j.X type=float32 kind=float32 quoted=false*/
{
if tok != fflib.FFTok_double && tok != fflib.FFTok_integer && tok != fflib.FFTok_null {
return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for float32", tok))
}
}
{
if tok == fflib.FFTok_null {
} else {
tval, err := fflib.ParseFloat(fs.Output.Bytes(), 32)
if err != nil {
return fs.WrapErr(err)
}
j.X = float32(tval)
}
}
state = fflib.FFParse_after_value
goto mainparse
handle_Y:
/* handler: j.Y type=float32 kind=float32 quoted=false*/
{
if tok != fflib.FFTok_double && tok != fflib.FFTok_integer && tok != fflib.FFTok_null {
return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for float32", tok))
}
}
{
if tok == fflib.FFTok_null {
} else {
tval, err := fflib.ParseFloat(fs.Output.Bytes(), 32)
if err != nil {
return fs.WrapErr(err)
}
j.Y = float32(tval)
}
}
state = fflib.FFParse_after_value
goto mainparse
handle_Z:
/* handler: j.Z type=float32 kind=float32 quoted=false*/
{
if tok != fflib.FFTok_double && tok != fflib.FFTok_integer && tok != fflib.FFTok_null {
return fs.WrapErr(fmt.Errorf("cannot unmarshal %s into Go value for float32", tok))
}
}
{
if tok == fflib.FFTok_null {
} else {
tval, err := fflib.ParseFloat(fs.Output.Bytes(), 32)
if err != nil {
return fs.WrapErr(err)
}
j.Z = float32(tval)
}
}
state = fflib.FFParse_after_value
goto mainparse
wantedvalue:
return fs.WrapErr(fmt.Errorf("wanted value token, but got token: %v", tok))
wrongtokenerror:
return fs.WrapErr(fmt.Errorf("ffjson: wanted token: %v, but got token: %v output=%s", wantedTok, tok, fs.Output.String()))
tokerror:
if fs.BigError != nil {
return fs.WrapErr(fs.BigError)
}
err = fs.Error.ToError()
if err != nil {
return fs.WrapErr(err)
}
panic("ffjson-generated: unreachable, please report bug.")
done:
return nil
}

View File

@ -8,32 +8,51 @@ import (
"github.com/francoispqt/gojay"
jsoniter "github.com/json-iterator/go"
"github.com/pquerna/ffjson/ffjson"
"xinu.tv/rustperf/src/ffjsonvec3"
)
type testFunc struct {
name string
f func([]Vec3) ([]byte, error)
f func(start, stop func(), vs []Vec3) ([]byte, error)
}
var encodeFuncs = []testFunc{
{"HandRolled", HandRolled},
{"stdlib", func(vs []Vec3) ([]byte, error) {
{"HandRolled", func(_, _ func(), vs []Vec3) ([]byte, error) {
return HandRolled(vs)
}},
{"stdlib", func(_, _ func(), vs []Vec3) ([]byte, error) {
return json.Marshal(vs)
}},
{"ffjson", func(vs []Vec3) ([]byte, error) {
{"ffjsonvec3", func(start, stop func(), vs []Vec3) ([]byte, error) {
stop()
var vecs []ffjsonvec3.Vec3
for _, v := range vs {
v := ffjsonvec3.Vec3{
X: v.X,
Y: v.Y,
Z: v.Z,
}
vecs = append(vecs, v)
}
start()
return ffjson.Marshal(vecs)
}},
{"ffjson", func(_, _ func(), vs []Vec3) ([]byte, error) {
return ffjson.Marshal(vs)
}},
{"iterator", func(vs []Vec3) ([]byte, error) {
{"iterator", func(_, _ func(), vs []Vec3) ([]byte, error) {
var js = jsoniter.ConfigCompatibleWithStandardLibrary
return js.Marshal(vs)
}},
{"gojay", func(vs []Vec3) ([]byte, error) {
{"gojay", func(start, stop func(), vs []Vec3) ([]byte, error) {
stop()
var vecs Vec3Slice
for _, v := range vs {
v := v
vecs = append(vecs, &v)
}
start()
return gojay.Marshal(&vecs)
}},
}
@ -46,7 +65,9 @@ func TestVec3ToJSON(t *testing.T) {
for _, ts := range encodeFuncs {
t.Run(ts.name, func(t *testing.T) {
got, err := ts.f(vecs)
stop := func() {}
start := func() {}
got, err := ts.f(start, stop, vecs)
if err != nil {
t.Fatalf("Failed to marshal: %v", err)
}
@ -69,8 +90,10 @@ func BenchmarkVec3ToJSON(b *testing.B) {
}
for _, ts := range encodeFuncs {
b.Run(ts.name, func(b *testing.B) {
stop := func() { b.StopTimer() }
start := func() { b.StartTimer() }
for i := 0; i < b.N; i++ {
if _, err := ts.f(vecs); err != nil {
if _, err := ts.f(start, stop, vecs); err != nil {
b.Fatalf("Failed to marshal: %v", err)
}
}