Skip to content

Commit

Permalink
codec: remove unnecessary allocations in ProtoCodec.MarshalBinaryLeng…
Browse files Browse the repository at this point in the history
…thPrefixed (#6877)

* codec: remove unnecessary allocations in ProtoCodec.MarshalBinaryLengthPrefixed

Improve ProtoCodec.MarshalBinaryLengthPrefixed by removing the need
to use a *bytes.Buffer and instead use an array and invoke
binary.PutUvarint, and directly create the respective length prefixed
concatentation.

With this change we get the following improvement in all dimenions
of throughput, bytes allocated, number of allocations per operation.

```shell
$ benchstat before.txt after.txt
name                                     old time/op    new time/op    delta
ProtoCodecMarshalBinaryLengthPrefixed-8     295ns ± 2%     177ns ± 3%  -39.92%  (p=0.000 n=20+20)

name                                     old speed      new speed      delta
ProtoCodecMarshalBinaryLengthPrefixed-8   505MB/s ± 2%   841MB/s ± 3%  +66.44%  (p=0.000 n=20+20)

name                                     old alloc/op   new alloc/op   delta
ProtoCodecMarshalBinaryLengthPrefixed-8      576B ± 0%      336B ± 0%  -41.67%  (p=0.000 n=20+20)

name                                     old allocs/op  new allocs/op  delta
ProtoCodecMarshalBinaryLengthPrefixed-8      5.00 ± 0%      3.00 ± 0%  -40.00%  (p=0.000 n=20+20)
```

Fixes #6875

* Address @marbar3778's feedback

Use binary.MaxVarintLen64 instead of 10

Co-authored-by: Marko <marbar3778@yahoo.com>

Co-authored-by: Marko <marbar3778@yahoo.com>
Co-authored-by: Aaron Craelius <aaron@regen.network>
  • Loading branch information
3 people authored Jul 29, 2020
1 parent c7dab51 commit cbb68fc
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 24 deletions.
14 changes: 1 addition & 13 deletions codec/codec.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package codec

import (
"encoding/binary"
"io"
"github.com/gogo/protobuf/proto"

"github.com/cosmos/cosmos-sdk/codec/types"

"github.com/gogo/protobuf/proto"
)

type (
Expand Down Expand Up @@ -58,12 +55,3 @@ type (
Unmarshal(data []byte) error
}
)

func encodeUvarint(w io.Writer, u uint64) (err error) {
var buf [10]byte

n := binary.PutUvarint(buf[:], u)
_, err = w.Write(buf[0:n])

return err
}
14 changes: 3 additions & 11 deletions codec/proto_codec.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package codec

import (
"bytes"
"encoding/binary"
"fmt"
"strings"
Expand Down Expand Up @@ -40,16 +39,9 @@ func (pc *ProtoCodec) MarshalBinaryLengthPrefixed(o ProtoMarshaler) ([]byte, err
return nil, err
}

buf := new(bytes.Buffer)
if err := encodeUvarint(buf, uint64(o.Size())); err != nil {
return nil, err
}

if _, err := buf.Write(bz); err != nil {
return nil, err
}

return buf.Bytes(), nil
var sizeBuf [binary.MaxVarintLen64]byte
n := binary.PutUvarint(sizeBuf[:], uint64(o.Size()))
return append(sizeBuf[:n], bz...), nil
}

func (pc *ProtoCodec) MustMarshalBinaryLengthPrefixed(o ProtoMarshaler) []byte {
Expand Down
43 changes: 43 additions & 0 deletions codec/proto_codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"testing"

"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/require"

"github.com/cosmos/cosmos-sdk/codec"
Expand Down Expand Up @@ -193,3 +194,45 @@ func TestProtoCodecUnmarshalBinaryLengthPrefixedChecks(t *testing.T) {
require.Panics(t, func() { cdc.MustUnmarshalBinaryLengthPrefixed(crafted, recv) })
})
}

func mustAny(msg proto.Message) *types.Any {
any, err := types.NewAnyWithValue(msg)
if err != nil {
panic(err)
}
return any
}

func BenchmarkProtoCodecMarshalBinaryLengthPrefixed(b *testing.B) {
var pCdc = codec.NewProtoCodec(types.NewInterfaceRegistry()).(*codec.ProtoCodec)
var msg = &testdata.HasAnimal{
X: 1000,
Animal: mustAny(&testdata.HasAnimal{
X: 2000,
Animal: mustAny(&testdata.HasAnimal{
X: 3000,
Animal: mustAny(&testdata.HasAnimal{
X: 4000,
Animal: mustAny(&testdata.HasAnimal{
X: 5000,
Animal: mustAny(&testdata.Cat{
Moniker: "Garfield",
Lives: 6,
}),
}),
}),
}),
}),
}

b.ResetTimer()
b.ReportAllocs()

for i := 0; i < b.N; i++ {
blob, err := pCdc.MarshalBinaryLengthPrefixed(msg)
if err != nil {
b.Fatal(err)
}
b.SetBytes(int64(len(blob)))
}
}

0 comments on commit cbb68fc

Please sign in to comment.