diff --git a/README.md b/README.md index f64379c9..a91c77d6 100644 --- a/README.md +++ b/README.md @@ -113,12 +113,14 @@ encoding and decoding. Benchmark source code can be found at: [https://github.com/nrwiersma/avro-benchmarks](https://github.com/nrwiersma/avro-benchmarks) ``` -BenchmarkGoAvroDecode-8 326304 3608 ns/op 442 B/op 27 allocs/op -BenchmarkGoAvroEncode-8 282902 4367 ns/op 856 B/op 63 allocs/op -BenchmarkHambaDecode-8 2133788 568 ns/op 64 B/op 4 allocs/op -BenchmarkHambaEncode-8 2635092 444 ns/op 112 B/op 1 allocs/op -BenchmarkLinkedinDecode-8 583681 2138 ns/op 1728 B/op 35 allocs/op -BenchmarkLinkedinEncode-8 1527082 785 ns/op 248 B/op 5 allocs/op +BenchmarkGoAvroDecode-10 495176 2413 ns/op 418 B/op 27 allocs/op +BenchmarkGoAvroEncode-10 420168 2917 ns/op 948 B/op 63 allocs/op +BenchmarkGoGenAvroDecode-10 757150 1552 ns/op 728 B/op 45 allocs/op +BenchmarkGoGenAvroEncode-10 1882940 639.0 ns/op 256 B/op 3 allocs/op +BenchmarkHambaDecode-10 3138063 383.0 ns/op 64 B/op 4 allocs/op +BenchmarkHambaEncode-10 4377513 273.3 ns/op 112 B/op 1 allocs/op +BenchmarkLinkedinDecode-10 1000000 1109 ns/op 1688 B/op 35 allocs/op +BenchmarkLinkedinEncode-10 2641016 456.0 ns/op 248 B/op 5 allocs/op ``` Always benchmark with your own workload. The result depends heavily on the data input. diff --git a/codec_record.go b/codec_record.go index 7b9acf93..a294b0ea 100644 --- a/codec_record.go +++ b/codec_record.go @@ -79,6 +79,11 @@ func decoderOfStruct(cfg *frozenConfig, schema Schema, typ reflect2.Type) ValDec return &structDecoder{typ: typ, fields: fields} } +type structFieldDecoder struct { + field []*reflect2.UnsafeStructField + decoder ValDecoder +} + type structDecoder struct { typ reflect2.Type fields []*structFieldDecoder @@ -86,44 +91,36 @@ type structDecoder struct { func (d *structDecoder) Decode(ptr unsafe.Pointer, r *Reader) { for _, field := range d.fields { - field.Decode(ptr, r) - } -} - -type structFieldDecoder struct { - field []*reflect2.UnsafeStructField - decoder ValDecoder -} + // Skip case + if field.field == nil { + field.decoder.Decode(nil, r) + continue + } -func (d *structFieldDecoder) Decode(ptr unsafe.Pointer, r *Reader) { - // Skip case - if d.field == nil { - d.decoder.Decode(nil, r) - return - } + fieldPtr := ptr + for i, f := range field.field { + fieldPtr = f.UnsafeGet(fieldPtr) - fieldPtr := ptr - for i, f := range d.field { - fieldPtr = f.UnsafeGet(fieldPtr) + if i == len(field.field)-1 { + break + } - if i == len(d.field)-1 { - break - } + if f.Type().Kind() == reflect.Ptr { + if *((*unsafe.Pointer)(ptr)) == nil { + newPtr := f.Type().UnsafeNew() + *((*unsafe.Pointer)(fieldPtr)) = newPtr + } - if f.Type().Kind() == reflect.Ptr { - if *((*unsafe.Pointer)(ptr)) == nil { - newPtr := f.Type().UnsafeNew() - *((*unsafe.Pointer)(fieldPtr)) = newPtr + fieldPtr = *((*unsafe.Pointer)(fieldPtr)) } - - fieldPtr = *((*unsafe.Pointer)(fieldPtr)) } - } - d.decoder.Decode(fieldPtr, r) + field.decoder.Decode(fieldPtr, r) - if r.Error != nil && !errors.Is(r.Error, io.EOF) { - for _, f := range d.field { - r.Error = fmt.Errorf("%s: %w", f.Name(), r.Error) + if r.Error != nil && !errors.Is(r.Error, io.EOF) { + for _, f := range field.field { + r.Error = fmt.Errorf("%s: %w", f.Name(), r.Error) + } + return } } } @@ -177,6 +174,12 @@ func encoderOfStruct(cfg *frozenConfig, schema Schema, typ reflect2.Type) ValEnc return &structEncoder{typ: typ, fields: fields} } +type structFieldEncoder struct { + field []*reflect2.UnsafeStructField + defaultPtr unsafe.Pointer + encoder ValEncoder +} + type structEncoder struct { typ reflect2.Type fields []*structFieldEncoder @@ -184,45 +187,35 @@ type structEncoder struct { func (e *structEncoder) Encode(ptr unsafe.Pointer, w *Writer) { for _, field := range e.fields { - field.Encode(ptr, w) - } -} - -type structFieldEncoder struct { - field []*reflect2.UnsafeStructField - defaultPtr unsafe.Pointer - encoder ValEncoder -} + // Default case + if field.field == nil { + field.encoder.Encode(field.defaultPtr, w) + continue + } -func (e *structFieldEncoder) Encode(ptr unsafe.Pointer, w *Writer) { - // Default case - if e.field == nil { - e.encoder.Encode(e.defaultPtr, w) - return - } + fieldPtr := ptr + for i, f := range field.field { + fieldPtr = f.UnsafeGet(fieldPtr) - fieldPtr := ptr - for i, f := range e.field { - fieldPtr = f.UnsafeGet(fieldPtr) + if i == len(field.field)-1 { + break + } - if i == len(e.field)-1 { - break - } + if f.Type().Kind() == reflect.Ptr { + if *((*unsafe.Pointer)(ptr)) == nil { + w.Error = fmt.Errorf("embedded field %q is nil", f.Name()) + return + } - if f.Type().Kind() == reflect.Ptr { - if *((*unsafe.Pointer)(ptr)) == nil { - w.Error = fmt.Errorf("embedded field %q is nil", f.Name()) - return + fieldPtr = *((*unsafe.Pointer)(fieldPtr)) } - - fieldPtr = *((*unsafe.Pointer)(fieldPtr)) } - } - e.encoder.Encode(fieldPtr, w) + field.encoder.Encode(fieldPtr, w) - if w.Error != nil && !errors.Is(w.Error, io.EOF) { - for _, f := range e.field { - w.Error = fmt.Errorf("%s: %w", f.Name(), w.Error) + if w.Error != nil && !errors.Is(w.Error, io.EOF) { + for _, f := range field.field { + w.Error = fmt.Errorf("%s: %w", f.Name(), w.Error) + } } } }