Skip to content

Commit

Permalink
feat: optimise struct codec (#133)
Browse files Browse the repository at this point in the history
  • Loading branch information
nrwiersma authored Nov 21, 2021
1 parent db3bb4d commit 090be76
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 69 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
119 changes: 56 additions & 63 deletions codec_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,51 +79,48 @@ 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
}

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
}
}
}
Expand Down Expand Up @@ -177,52 +174,48 @@ 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
}

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)
}
}
}
}
Expand Down

0 comments on commit 090be76

Please sign in to comment.