Closed KnBrBz closed 2 years ago
Can you provide the autogenerated code or the steps to autogenerate please?
I am using this package Auto generated code
// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT.
package gjsonvseasy
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 easyjsonC80ae7adDecodeGitlabOriginSunsoftProSnippetsGjsonVsEasy(in *jlexer.Lexer, out *SampleJSON) {
isTopLevel := in.IsStart()
if in.IsNull() {
if isTopLevel {
in.Consumed()
}
in.Skip()
return
}
in.Delim('{')
for !in.IsDelim('}') {
key := in.UnsafeFieldName(false)
in.WantColon()
if in.IsNull() {
in.Skip()
in.WantComma()
continue
}
switch key {
case "s":
out.S = string(in.String())
case "i":
out.I = int(in.Int())
case "b":
out.B = float64(in.Float64())
case "a":
out.A = float64(in.Float64())
case "m":
out.M = float64(in.Float64())
case "mb":
out.MB = float64(in.Float64())
case "p":
out.P = float64(in.Float64())
case "pb":
out.PB = float64(in.Float64())
case "t":
out.T = int64(in.Int64())
case "d":
out.D = int(in.Int())
case "fi":
out.FI = int(in.Int())
case "qs":
out.QS = bool(in.Bool())
case "ts":
out.TS = bool(in.Bool())
default:
in.SkipRecursive()
}
in.WantComma()
}
in.Delim('}')
if isTopLevel {
in.Consumed()
}
}
func easyjsonC80ae7adEncodeGitlabOriginSunsoftProSnippetsGjsonVsEasy(out *jwriter.Writer, in SampleJSON) {
out.RawByte('{')
first := true
_ = first
{
const prefix string = ",\"s\":"
out.RawString(prefix[1:])
out.String(string(in.S))
}
{
const prefix string = ",\"i\":"
out.RawString(prefix)
out.Int(int(in.I))
}
{
const prefix string = ",\"b\":"
out.RawString(prefix)
out.Float64(float64(in.B))
}
{
const prefix string = ",\"a\":"
out.RawString(prefix)
out.Float64(float64(in.A))
}
{
const prefix string = ",\"m\":"
out.RawString(prefix)
out.Float64(float64(in.M))
}
{
const prefix string = ",\"mb\":"
out.RawString(prefix)
out.Float64(float64(in.MB))
}
{
const prefix string = ",\"p\":"
out.RawString(prefix)
out.Float64(float64(in.P))
}
{
const prefix string = ",\"pb\":"
out.RawString(prefix)
out.Float64(float64(in.PB))
}
{
const prefix string = ",\"t\":"
out.RawString(prefix)
out.Int64(int64(in.T))
}
{
const prefix string = ",\"d\":"
out.RawString(prefix)
out.Int(int(in.D))
}
{
const prefix string = ",\"fi\":"
out.RawString(prefix)
out.Int(int(in.FI))
}
{
const prefix string = ",\"qs\":"
out.RawString(prefix)
out.Bool(bool(in.QS))
}
{
const prefix string = ",\"ts\":"
out.RawString(prefix)
out.Bool(bool(in.TS))
}
out.RawByte('}')
}
// MarshalJSON supports json.Marshaler interface
func (v SampleJSON) MarshalJSON() ([]byte, error) {
w := jwriter.Writer{}
easyjsonC80ae7adEncodeGitlabOriginSunsoftProSnippetsGjsonVsEasy(&w, v)
return w.Buffer.BuildBytes(), w.Error
}
// MarshalEasyJSON supports easyjson.Marshaler interface
func (v SampleJSON) MarshalEasyJSON(w *jwriter.Writer) {
easyjsonC80ae7adEncodeGitlabOriginSunsoftProSnippetsGjsonVsEasy(w, v)
}
// UnmarshalJSON supports json.Unmarshaler interface
func (v *SampleJSON) UnmarshalJSON(data []byte) error {
r := jlexer.Lexer{Data: data}
easyjsonC80ae7adDecodeGitlabOriginSunsoftProSnippetsGjsonVsEasy(&r, v)
return r.Error()
}
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
func (v *SampleJSON) UnmarshalEasyJSON(l *jlexer.Lexer) {
easyjsonC80ae7adDecodeGitlabOriginSunsoftProSnippetsGjsonVsEasy(l, v)
}
It can be created via the command
easyjson -all model.go
if SampleJSON
struct is defined in model.go
Thanks for providing the autogenerated code.
I can confirm seeing similar results as you.
goos: darwin
goarch: arm64
pkg: example.com/gjsonvseasy
BenchmarkEasyjson-10 1560355 757.7 ns/op 16 B/op 1 allocs/op
BenchmarkGjsonCheck-10 471795 2521 ns/op 136 B/op 10 allocs/op
BenchmarkGjsonNoCheck-10 469798 2520 ns/op 136 B/op 10 allocs/op
BenchmarkGjsonMany-10 423038 2769 ns/op 1288 B/op 11 allocs/op
I don't recommend gjson for hydrating a structured model. GJSON is intended to be used for querying arbitrary json for specific values. It works by searching the json document as quickly as possible, and returning the found value right away.
Trying to fill a struct using GJSON, as in the example you show, means that every field needs a gjson.Get
operation. Each Get
operation starts from the beginning of the json document in search in search of the value. That's can be pretty expensive when dealing with lots of fields.
Maybe I am not using it properly?
They're probably a few things you can do differently.
First is that gjson enjoys working on strings, not bytes. The gjson.GetBytes
is a convenience library that will likely result in at least one extra allocation due to a byte->string conversion. By converting the bytes to a string before the doing the benchmark will give a little boost and avoid the extra allocations.
Second use ForEach
instead of a bunch of Get
operations. This allows for stream-style parsing, for example:
func BenchmarkGjsonForEach(b *testing.B) {
var decodedData SampleJSON
str := string(bytes)
for i := 0; i < b.N; i++ {
gjson.Parse(str).ForEach(func(k, v gjson.Result) bool {
switch k.String() {
case "s":
decodedData.S = v.String()
case "i":
decodedData.I = int(v.Int())
case "b":
decodedData.B = v.Float()
case "a":
decodedData.A = v.Float()
case "m":
decodedData.M = v.Float()
case "mb":
decodedData.MB = v.Float()
case "p":
decodedData.P = v.Float()
case "pb":
decodedData.PB = v.Float()
case "t":
decodedData.T = v.Int()
case "d":
decodedData.D = int(v.Int())
case "fi":
decodedData.FI = int(v.Int())
case "qs":
decodedData.QS = v.Bool()
case "ts":
decodedData.TS = v.Bool()
}
return true
})
}
result = decodedData
}
But again, for this type of use case, I recommend just sticking with an unmarshaller library like easyjson
.
Here are my results:
goos: darwin
goarch: arm64
BenchmarkEasyjson-10 1549269 762.6 ns/op 16 B/op 1 allocs/op
BenchmarkGjsonCheck-10 471752 2526 ns/op 136 B/op 10 allocs/op
BenchmarkGjsonNoCheck-10 475734 2524 ns/op 136 B/op 10 allocs/op
BenchmarkGjsonMany-10 421005 2752 ns/op 1288 B/op 11 allocs/op
BenchmarkGjsonCheckString-10 540081 2163 ns/op 0 B/op 0 allocs/op
BenchmarkGjsonNoCheckString-10 551227 2169 ns/op 0 B/op 0 allocs/op
BenchmarkGjsonForEach-10 1421556 846.6 ns/op 0 B/op 0 allocs/op
``
Thank you for answer. Closing ticket.
With benchmark (auto generated code is omitted)
I am getting such results
Maybe I am not using it properly?