diff --git a/benchmarks/bench_test.go b/benchmarks/bench_test.go index 438ff03..e199620 100644 --- a/benchmarks/bench_test.go +++ b/benchmarks/bench_test.go @@ -24,6 +24,8 @@ import ( "github.com/goccy/go-json" jsoniter "github.com/json-iterator/go" + segmentiojson "github.com/segmentio/encoding/json" + "github.com/wI2L/jettison" ) type codeResponse struct { @@ -94,6 +96,24 @@ func codeInit() { } } +func Benchmark_EncodeBigData_GoJson(b *testing.B) { + b.ReportAllocs() + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + b.RunParallel(func(pb *testing.PB) { + enc := json.NewEncoder(ioutil.Discard) + for pb.Next() { + if err := enc.Encode(&codeStruct); err != nil { + b.Fatal("Encode:", err) + } + } + }) + b.SetBytes(int64(len(codeJSON))) +} + func Benchmark_EncodeBigData_EncodingJson(b *testing.B) { b.ReportAllocs() if codeJSON == nil { @@ -131,7 +151,7 @@ func Benchmark_EncodeBigData_JsonIter(b *testing.B) { b.SetBytes(int64(len(codeJSON))) } -func Benchmark_EncodeBigData_GoJson(b *testing.B) { +func Benchmark_EncodeBigData_SegmentioJson(b *testing.B) { b.ReportAllocs() if codeJSON == nil { b.StopTimer() @@ -139,7 +159,7 @@ func Benchmark_EncodeBigData_GoJson(b *testing.B) { b.StartTimer() } b.RunParallel(func(pb *testing.PB) { - enc := json.NewEncoder(ioutil.Discard) + enc := segmentiojson.NewEncoder(ioutil.Discard) for pb.Next() { if err := enc.Encode(&codeStruct); err != nil { b.Fatal("Encode:", err) @@ -149,6 +169,24 @@ func Benchmark_EncodeBigData_GoJson(b *testing.B) { b.SetBytes(int64(len(codeJSON))) } +func Benchmark_MarshalBigData_GoJson(b *testing.B) { + b.ReportAllocs() + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if _, err := json.Marshal(&codeStruct); err != nil { + b.Fatal("Marshal:", err) + } + } + }) + b.SetBytes(int64(len(codeJSON))) +} + func Benchmark_MarshalBigData_EncodingJson(b *testing.B) { b.ReportAllocs() if codeJSON == nil { @@ -184,7 +222,7 @@ func Benchmark_MarshalBigData_JsonIter(b *testing.B) { b.SetBytes(int64(len(codeJSON))) } -func Benchmark_MarshalBigData_GoJson(b *testing.B) { +func Benchmark_MarshalBigData_Jettison(b *testing.B) { b.ReportAllocs() if codeJSON == nil { b.StopTimer() @@ -193,7 +231,24 @@ func Benchmark_MarshalBigData_GoJson(b *testing.B) { } b.RunParallel(func(pb *testing.PB) { for pb.Next() { - if _, err := json.Marshal(&codeStruct); err != nil { + if _, err := jettison.Marshal(&codeStruct); err != nil { + b.Fatal("Marshal:", err) + } + } + }) + b.SetBytes(int64(len(codeJSON))) +} + +func Benchmark_MarshalBigData_SegmentioJson(b *testing.B) { + b.ReportAllocs() + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if _, err := segmentiojson.Marshal(&codeStruct); err != nil { b.Fatal("Marshal:", err) } } diff --git a/benchmarks/encode_test.go b/benchmarks/encode_test.go index 683fe0b..9e94c37 100644 --- a/benchmarks/encode_test.go +++ b/benchmarks/encode_test.go @@ -8,6 +8,7 @@ import ( gojson "github.com/goccy/go-json" jsoniter "github.com/json-iterator/go" segmentiojson "github.com/segmentio/encoding/json" + "github.com/wI2L/jettison" ) func Benchmark_Encode_SmallStruct_EncodingJson(b *testing.B) { @@ -29,6 +30,24 @@ func Benchmark_Encode_SmallStruct_JsonIter(b *testing.B) { } } +func Benchmark_Encode_SmallStruct_EasyJson(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := NewSmallPayloadEasyJson().MarshalJSON(); err != nil { + b.Fatal(err) + } + } +} + +func Benchmark_Encode_SmallStruct_Jettison(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := jettison.Marshal(NewSmallPayload()); err != nil { + b.Fatal(err) + } + } +} + func Benchmark_Encode_SmallStruct_GoJay(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -86,6 +105,26 @@ func Benchmark_Encode_SmallStructCached_JsonIter(b *testing.B) { } } +func Benchmark_Encode_SmallStructCached_EasyJson(b *testing.B) { + cached := NewSmallPayloadEasyJson() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := cached.MarshalJSON(); err != nil { + b.Fatal(err) + } + } +} + +func Benchmark_Encode_SmallStructCached_Jettison(b *testing.B) { + cached := NewSmallPayload() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := jettison.Marshal(cached); err != nil { + b.Fatal(err) + } + } +} + func Benchmark_Encode_SmallStructCached_GoJay(b *testing.B) { cached := NewSmallPayload() b.ReportAllocs() @@ -145,6 +184,24 @@ func Benchmark_Encode_MediumStruct_JsonIter(b *testing.B) { } } +func Benchmark_Encode_MediumStruct_EasyJson(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := NewMediumPayloadEasyJson().MarshalJSON(); err != nil { + b.Fatal(err) + } + } +} + +func Benchmark_Encode_MediumStruct_Jettison(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := jettison.Marshal(NewMediumPayload()); err != nil { + b.Fatal(err) + } + } +} + func Benchmark_Encode_MediumStruct_GoJay(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -202,6 +259,26 @@ func Benchmark_Encode_MediumStructCached_JsonIter(b *testing.B) { } } +func Benchmark_Encode_MediumStructCached_EasyJson(b *testing.B) { + cached := NewMediumPayloadEasyJson() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := cached.MarshalJSON(); err != nil { + b.Fatal(err) + } + } +} + +func Benchmark_Encode_MediumStructCached_Jettison(b *testing.B) { + cached := NewMediumPayload() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := jettison.Marshal(cached); err != nil { + b.Fatal(err) + } + } +} + func Benchmark_Encode_MediumStructCached_GoJay(b *testing.B) { cached := NewMediumPayload() b.ReportAllocs() @@ -261,6 +338,24 @@ func Benchmark_Encode_LargeStruct_JsonIter(b *testing.B) { } } +func Benchmark_Encode_LargeStruct_EasyJson(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := NewLargePayloadEasyJson().MarshalJSON(); err != nil { + b.Fatal(err) + } + } +} + +func Benchmark_Encode_LargeStruct_Jettison(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := jettison.Marshal(NewLargePayload()); err != nil { + b.Fatal(err) + } + } +} + func Benchmark_Encode_LargeStruct_GoJay(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { @@ -318,6 +413,26 @@ func Benchmark_Encode_LargeStructCached_JsonIter(b *testing.B) { } } +func Benchmark_Encode_LargeStructCached_EasyJson(b *testing.B) { + cached := NewLargePayloadEasyJson() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := cached.MarshalJSON(); err != nil { + b.Fatal(err) + } + } +} + +func Benchmark_Encode_LargeStructCached_Jettison(b *testing.B) { + cached := NewLargePayload() + b.ReportAllocs() + for i := 0; i < b.N; i++ { + if _, err := jettison.Marshal(cached); err != nil { + b.Fatal(err) + } + } +} + func Benchmark_Encode_LargeStructCached_GoJay(b *testing.B) { cached := NewLargePayload() b.ReportAllocs() diff --git a/benchmarks/go.mod b/benchmarks/go.mod index a4af8ae..5872cac 100644 --- a/benchmarks/go.mod +++ b/benchmarks/go.mod @@ -6,7 +6,9 @@ require ( github.com/francoispqt/gojay v1.2.13 github.com/goccy/go-json v0.0.0-00010101000000-000000000000 github.com/json-iterator/go v1.1.9 + github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe github.com/segmentio/encoding v0.2.4 + github.com/wI2L/jettison v0.7.1 ) replace github.com/goccy/go-json => ../ diff --git a/benchmarks/go.sum b/benchmarks/go.sum index 1aa81f2..e2454fa 100644 --- a/benchmarks/go.sum +++ b/benchmarks/go.sum @@ -14,6 +14,7 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= 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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -54,7 +55,9 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= @@ -66,6 +69,7 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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= @@ -75,6 +79,7 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/segmentio/encoding v0.1.10/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw= github.com/segmentio/encoding v0.2.4 h1:TQRXhTlXj4urZe3Z5QVgxs9Ad1i7GYHg9peAtjOPe28= github.com/segmentio/encoding v0.2.4/go.mod h1:MJjRE6bMDocliO2FyFC2Dusp+uYdBfHWh5Bw7QyExto= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= @@ -106,9 +111,12 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ 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/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/wI2L/jettison v0.7.1 h1:XNq/WvSOAiJhFww9F5JZZcBZtKFL2Y/9WHHEHLDq9TE= +github.com/wI2L/jettison v0.7.1/go.mod h1:dj49nOP41M7x6Jql62BqqF/+nW+XJgBaWzJR0hd6M84= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= @@ -166,9 +174,11 @@ google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9M google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/benchmarks/large_payload.go b/benchmarks/large_payload.go index 4fa2648..f6d1c1a 100644 --- a/benchmarks/large_payload.go +++ b/benchmarks/large_payload.go @@ -119,6 +119,12 @@ type LargePayload struct { Topics *DSTopicsList } +//easyjson:json +type LargePayloadEasyJson struct { + Users DSUsers + Topics *DSTopicsList +} + func (m *LargePayload) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "users": @@ -134,7 +140,6 @@ func (m *LargePayload) NKeys() int { return 2 } -//easyjson:json func (m *LargePayload) MarshalJSONObject(enc *gojay.Encoder) { enc.AddArrayKey("users", &m.Users) enc.AddObjectKey("topics", m.Topics) @@ -175,3 +180,31 @@ func NewLargePayload() *LargePayload { }, } } + +func NewLargePayloadEasyJson() *LargePayloadEasyJson { + dsUsers := DSUsers{} + dsTopics := DSTopics{} + for i := 0; i < 100; i++ { + str := "test" + strconv.Itoa(i) + dsUsers = append( + dsUsers, + &DSUser{ + Username: str, + }, + ) + dsTopics = append( + dsTopics, + &DSTopic{ + Id: i, + Slug: str, + }, + ) + } + return &LargePayloadEasyJson{ + Users: dsUsers, + Topics: &DSTopicsList{ + Topics: dsTopics, + MoreTopicsUrl: "http://test.com", + }, + } +} diff --git a/benchmarks/large_payload_easyjson.go b/benchmarks/large_payload_easyjson.go new file mode 100644 index 0000000..f032176 --- /dev/null +++ b/benchmarks/large_payload_easyjson.go @@ -0,0 +1,367 @@ +// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. + +package benchmark + +import ( + json "encoding/json" + easyjson "github.com/mailru/easyjson" + jlexer "github.com/mailru/easyjson/jlexer" + jwriter "github.com/mailru/easyjson/jwriter" +) + +// suppress unused package warning +var ( + _ *json.RawMessage + _ *jlexer.Lexer + _ *jwriter.Writer + _ easyjson.Marshaler +) + +func easyjsonD519278DecodeBenchmark(in *jlexer.Lexer, out *LargePayloadEasyJson) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Users": + if in.IsNull() { + in.Skip() + out.Users = nil + } else { + in.Delim('[') + if out.Users == nil { + if !in.IsDelim(']') { + out.Users = make(DSUsers, 0, 8) + } else { + out.Users = DSUsers{} + } + } else { + out.Users = (out.Users)[:0] + } + for !in.IsDelim(']') { + var v1 *DSUser + if in.IsNull() { + in.Skip() + v1 = nil + } else { + if v1 == nil { + v1 = new(DSUser) + } + easyjsonD519278DecodeBenchmark1(in, v1) + } + out.Users = append(out.Users, v1) + in.WantComma() + } + in.Delim(']') + } + case "Topics": + if in.IsNull() { + in.Skip() + out.Topics = nil + } else { + if out.Topics == nil { + out.Topics = new(DSTopicsList) + } + easyjsonD519278DecodeBenchmark2(in, out.Topics) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonD519278EncodeBenchmark(out *jwriter.Writer, in LargePayloadEasyJson) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Users\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + if in.Users == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v2, v3 := range in.Users { + if v2 > 0 { + out.RawByte(',') + } + if v3 == nil { + out.RawString("null") + } else { + easyjsonD519278EncodeBenchmark1(out, *v3) + } + } + out.RawByte(']') + } + } + { + const prefix string = ",\"Topics\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + if in.Topics == nil { + out.RawString("null") + } else { + easyjsonD519278EncodeBenchmark2(out, *in.Topics) + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v LargePayloadEasyJson) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonD519278EncodeBenchmark(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v LargePayloadEasyJson) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonD519278EncodeBenchmark(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *LargePayloadEasyJson) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonD519278DecodeBenchmark(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *LargePayloadEasyJson) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonD519278DecodeBenchmark(l, v) +} +func easyjsonD519278DecodeBenchmark2(in *jlexer.Lexer, out *DSTopicsList) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Topics": + if in.IsNull() { + in.Skip() + out.Topics = nil + } else { + in.Delim('[') + if out.Topics == nil { + if !in.IsDelim(']') { + out.Topics = make(DSTopics, 0, 8) + } else { + out.Topics = DSTopics{} + } + } else { + out.Topics = (out.Topics)[:0] + } + for !in.IsDelim(']') { + var v4 *DSTopic + if in.IsNull() { + in.Skip() + v4 = nil + } else { + if v4 == nil { + v4 = new(DSTopic) + } + easyjsonD519278DecodeBenchmark3(in, v4) + } + out.Topics = append(out.Topics, v4) + in.WantComma() + } + in.Delim(']') + } + case "MoreTopicsUrl": + out.MoreTopicsUrl = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonD519278EncodeBenchmark2(out *jwriter.Writer, in DSTopicsList) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Topics\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + if in.Topics == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v5, v6 := range in.Topics { + if v5 > 0 { + out.RawByte(',') + } + if v6 == nil { + out.RawString("null") + } else { + easyjsonD519278EncodeBenchmark3(out, *v6) + } + } + out.RawByte(']') + } + } + { + const prefix string = ",\"MoreTopicsUrl\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.MoreTopicsUrl)) + } + out.RawByte('}') +} +func easyjsonD519278DecodeBenchmark3(in *jlexer.Lexer, out *DSTopic) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Id": + out.Id = int(in.Int()) + case "Slug": + out.Slug = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonD519278EncodeBenchmark3(out *jwriter.Writer, in DSTopic) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Id\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Id)) + } + { + const prefix string = ",\"Slug\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Slug)) + } + out.RawByte('}') +} +func easyjsonD519278DecodeBenchmark1(in *jlexer.Lexer, out *DSUser) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Username": + out.Username = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjsonD519278EncodeBenchmark1(out *jwriter.Writer, in DSUser) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Username\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Username)) + } + out.RawByte('}') +} diff --git a/benchmarks/medium_payload.go b/benchmarks/medium_payload.go index 93cc4df..55f0581 100644 --- a/benchmarks/medium_payload.go +++ b/benchmarks/medium_payload.go @@ -243,12 +243,17 @@ func (m *CBPerson) IsNil() bool { return m == nil } -//easyjson:json type MediumPayload struct { Person *CBPerson `json:"person"` Company string `json:"company"` } +//easyjson:json +type MediumPayloadEasyJson struct { + Person *CBPerson `json:"person"` + Company string `json:"company"` +} + func (m *MediumPayload) UnmarshalJSONObject(dec *gojay.Decoder, key string) error { switch key { case "person": @@ -314,3 +319,45 @@ func NewMediumPayload() *MediumPayload { }, } } + +func NewMediumPayloadEasyJson() *MediumPayloadEasyJson { + return &MediumPayloadEasyJson{ + Company: "test", + Person: &CBPerson{ + Name: &CBName{ + FullName: "test", + }, + Github: &CBGithub{ + Followers: 100, + }, + Gravatar: &CBGravatar{ + Avatars: Avatars{ + &CBAvatar{ + Url: "http://test.com", + }, + &CBAvatar{ + Url: "http://test.com", + }, + &CBAvatar{ + Url: "http://test.com", + }, + &CBAvatar{ + Url: "http://test.com", + }, + &CBAvatar{ + Url: "http://test.com", + }, + &CBAvatar{ + Url: "http://test.com", + }, + &CBAvatar{ + Url: "http://test.com", + }, + &CBAvatar{ + Url: "http://test.com", + }, + }, + }, + }, + } +} diff --git a/benchmarks/medium_payload_easyjson.go b/benchmarks/medium_payload_easyjson.go new file mode 100644 index 0000000..ca5be89 --- /dev/null +++ b/benchmarks/medium_payload_easyjson.go @@ -0,0 +1,453 @@ +// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. + +package benchmark + +import ( + json "encoding/json" + easyjson "github.com/mailru/easyjson" + jlexer "github.com/mailru/easyjson/jlexer" + jwriter "github.com/mailru/easyjson/jwriter" +) + +// suppress unused package warning +var ( + _ *json.RawMessage + _ *jlexer.Lexer + _ *jwriter.Writer + _ easyjson.Marshaler +) + +func easyjson8ca7813eDecodeBenchmark(in *jlexer.Lexer, out *MediumPayloadEasyJson) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "person": + if in.IsNull() { + in.Skip() + out.Person = nil + } else { + if out.Person == nil { + out.Person = new(CBPerson) + } + easyjson8ca7813eDecodeBenchmark1(in, out.Person) + } + case "company": + out.Company = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson8ca7813eEncodeBenchmark(out *jwriter.Writer, in MediumPayloadEasyJson) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"person\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + if in.Person == nil { + out.RawString("null") + } else { + easyjson8ca7813eEncodeBenchmark1(out, *in.Person) + } + } + { + const prefix string = ",\"company\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Company)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v MediumPayloadEasyJson) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson8ca7813eEncodeBenchmark(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v MediumPayloadEasyJson) MarshalEasyJSON(w *jwriter.Writer) { + easyjson8ca7813eEncodeBenchmark(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *MediumPayloadEasyJson) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson8ca7813eDecodeBenchmark(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *MediumPayloadEasyJson) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson8ca7813eDecodeBenchmark(l, v) +} +func easyjson8ca7813eDecodeBenchmark1(in *jlexer.Lexer, out *CBPerson) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "name": + if in.IsNull() { + in.Skip() + out.Name = nil + } else { + if out.Name == nil { + out.Name = new(CBName) + } + easyjson8ca7813eDecodeBenchmark2(in, out.Name) + } + case "github": + if in.IsNull() { + in.Skip() + out.Github = nil + } else { + if out.Github == nil { + out.Github = new(CBGithub) + } + easyjson8ca7813eDecodeBenchmark3(in, out.Github) + } + case "Gravatar": + if in.IsNull() { + in.Skip() + out.Gravatar = nil + } else { + if out.Gravatar == nil { + out.Gravatar = new(CBGravatar) + } + easyjson8ca7813eDecodeBenchmark4(in, out.Gravatar) + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson8ca7813eEncodeBenchmark1(out *jwriter.Writer, in CBPerson) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"name\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + if in.Name == nil { + out.RawString("null") + } else { + easyjson8ca7813eEncodeBenchmark2(out, *in.Name) + } + } + { + const prefix string = ",\"github\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + if in.Github == nil { + out.RawString("null") + } else { + easyjson8ca7813eEncodeBenchmark3(out, *in.Github) + } + } + { + const prefix string = ",\"Gravatar\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + if in.Gravatar == nil { + out.RawString("null") + } else { + easyjson8ca7813eEncodeBenchmark4(out, *in.Gravatar) + } + } + out.RawByte('}') +} +func easyjson8ca7813eDecodeBenchmark4(in *jlexer.Lexer, out *CBGravatar) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Avatars": + if in.IsNull() { + in.Skip() + out.Avatars = nil + } else { + in.Delim('[') + if out.Avatars == nil { + if !in.IsDelim(']') { + out.Avatars = make(Avatars, 0, 8) + } else { + out.Avatars = Avatars{} + } + } else { + out.Avatars = (out.Avatars)[:0] + } + for !in.IsDelim(']') { + var v1 *CBAvatar + if in.IsNull() { + in.Skip() + v1 = nil + } else { + if v1 == nil { + v1 = new(CBAvatar) + } + easyjson8ca7813eDecodeBenchmark5(in, v1) + } + out.Avatars = append(out.Avatars, v1) + in.WantComma() + } + in.Delim(']') + } + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson8ca7813eEncodeBenchmark4(out *jwriter.Writer, in CBGravatar) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Avatars\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + if in.Avatars == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v2, v3 := range in.Avatars { + if v2 > 0 { + out.RawByte(',') + } + if v3 == nil { + out.RawString("null") + } else { + easyjson8ca7813eEncodeBenchmark5(out, *v3) + } + } + out.RawByte(']') + } + } + out.RawByte('}') +} +func easyjson8ca7813eDecodeBenchmark5(in *jlexer.Lexer, out *CBAvatar) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Url": + out.Url = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson8ca7813eEncodeBenchmark5(out *jwriter.Writer, in CBAvatar) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Url\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Url)) + } + out.RawByte('}') +} +func easyjson8ca7813eDecodeBenchmark3(in *jlexer.Lexer, out *CBGithub) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "Followers": + out.Followers = int(in.Int()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson8ca7813eEncodeBenchmark3(out *jwriter.Writer, in CBGithub) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"Followers\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Followers)) + } + out.RawByte('}') +} +func easyjson8ca7813eDecodeBenchmark2(in *jlexer.Lexer, out *CBName) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "fullName": + out.FullName = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson8ca7813eEncodeBenchmark2(out *jwriter.Writer, in CBName) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"fullName\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.FullName)) + } + out.RawByte('}') +} diff --git a/benchmarks/small_payload.go b/benchmarks/small_payload.go index d1ec087..8e29f2d 100644 --- a/benchmarks/small_payload.go +++ b/benchmarks/small_payload.go @@ -4,7 +4,6 @@ import "github.com/francoispqt/gojay" var SmallFixture = []byte(`{"st": 1,"sid": 486,"tt": "active","gr": 0,"uuid": "de305d54-75b4-431b-adb2-eb6b9e546014","ip": "127.0.0.1","ua": "user_agent","tz": -6,"v": 1}`) -//easyjson:json type SmallPayload struct { St int Sid int @@ -17,6 +16,19 @@ type SmallPayload struct { V int } +//easyjson:json +type SmallPayloadEasyJson struct { + St int + Sid int + Tt string + Gr int + Uuid string + Ip string + Ua string + Tz int + V int +} + func (t *SmallPayload) MarshalJSONObject(enc *gojay.Encoder) { enc.AddIntKey("st", t.St) enc.AddIntKey("sid", t.Sid) @@ -74,3 +86,17 @@ func NewSmallPayload() *SmallPayload { V: 6, } } + +func NewSmallPayloadEasyJson() *SmallPayloadEasyJson { + return &SmallPayloadEasyJson{ + St: 1, + Sid: 2, + Tt: "TestString", + Gr: 4, + Uuid: "8f9a65eb-4807-4d57-b6e0-bda5d62f1429", + Ip: "127.0.0.1", + Ua: "Mozilla", + Tz: 8, + V: 6, + } +} diff --git a/benchmarks/small_payload_easyjson.go b/benchmarks/small_payload_easyjson.go new file mode 100644 index 0000000..86dad2b --- /dev/null +++ b/benchmarks/small_payload_easyjson.go @@ -0,0 +1,186 @@ +// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. + +package benchmark + +import ( + json "encoding/json" + easyjson "github.com/mailru/easyjson" + jlexer "github.com/mailru/easyjson/jlexer" + jwriter "github.com/mailru/easyjson/jwriter" +) + +// suppress unused package warning +var ( + _ *json.RawMessage + _ *jlexer.Lexer + _ *jwriter.Writer + _ easyjson.Marshaler +) + +func easyjson21677a1cDecodeBenchmark(in *jlexer.Lexer, out *SmallPayloadEasyJson) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeString() + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "St": + out.St = int(in.Int()) + case "Sid": + out.Sid = int(in.Int()) + case "Tt": + out.Tt = string(in.String()) + case "Gr": + out.Gr = int(in.Int()) + case "Uuid": + out.Uuid = string(in.String()) + case "Ip": + out.Ip = string(in.String()) + case "Ua": + out.Ua = string(in.String()) + case "Tz": + out.Tz = int(in.Int()) + case "V": + out.V = int(in.Int()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson21677a1cEncodeBenchmark(out *jwriter.Writer, in SmallPayloadEasyJson) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"St\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.St)) + } + { + const prefix string = ",\"Sid\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Sid)) + } + { + const prefix string = ",\"Tt\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Tt)) + } + { + const prefix string = ",\"Gr\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Gr)) + } + { + const prefix string = ",\"Uuid\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Uuid)) + } + { + const prefix string = ",\"Ip\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Ip)) + } + { + const prefix string = ",\"Ua\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.String(string(in.Ua)) + } + { + const prefix string = ",\"Tz\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.Tz)) + } + { + const prefix string = ",\"V\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + out.Int(int(in.V)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v SmallPayloadEasyJson) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson21677a1cEncodeBenchmark(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v SmallPayloadEasyJson) MarshalEasyJSON(w *jwriter.Writer) { + easyjson21677a1cEncodeBenchmark(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *SmallPayloadEasyJson) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson21677a1cDecodeBenchmark(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *SmallPayloadEasyJson) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson21677a1cDecodeBenchmark(l, v) +}