From 291a29af1e1f70bbe16574b3d53dafaee19bc82f Mon Sep 17 00:00:00 2001 From: Evgeniy Stratonikov Date: Fri, 6 Aug 2021 11:55:43 +0300 Subject: [PATCH] *: do not use `WriteArray` for frequently used items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `WriteArray` involves reflection, it makes sense to optimize serialization of transactions and application logs which are serialized constantly. Adding case in a type switch in `WriteArray` is not an option because we don't want new dependencies for `io` package. ``` name old time/op new time/op delta AppExecResult_EncodeBinary-8 852ns ± 3% 656ns ± 2% -22.94% (p=0.000 n=10+9) name old alloc/op new alloc/op delta AppExecResult_EncodeBinary-8 448B ± 0% 376B ± 0% -16.07% (p=0.000 n=10+10) name old allocs/op new allocs/op delta AppExecResult_EncodeBinary-8 7.00 ± 0% 5.00 ± 0% -28.57% (p=0.000 n=10+10) ``` ``` name old time/op new time/op delta Transaction_Bytes-8 1.29µs ± 3% 0.76µs ± 5% -41.52% (p=0.000 n=9+10) name old alloc/op new alloc/op delta Transaction_Bytes-8 1.21kB ± 0% 1.01kB ± 0% -16.56% (p=0.000 n=10+10) name old allocs/op new allocs/op delta Transaction_Bytes-8 12.0 ± 0% 7.0 ± 0% -41.67% (p=0.000 n=10+10) ``` Signed-off-by: Evgeniy Stratonikov --- pkg/core/state/notification_event.go | 5 ++++- pkg/core/state/notification_event_test.go | 25 +++++++++++++++++++++++ pkg/core/transaction/bench_test.go | 11 ++++++++++ pkg/core/transaction/transaction.go | 15 +++++++++++--- 4 files changed, 52 insertions(+), 4 deletions(-) diff --git a/pkg/core/state/notification_event.go b/pkg/core/state/notification_event.go index 5c7962ca0..c40f63e35 100644 --- a/pkg/core/state/notification_event.go +++ b/pkg/core/state/notification_event.go @@ -61,7 +61,10 @@ func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) { for _, it := range aer.Stack { stackitem.EncodeBinaryProtected(it, w) } - w.WriteArray(aer.Events) + w.WriteVarUint(uint64(len(aer.Events))) + for i := range aer.Events { + aer.Events[i].EncodeBinary(w) + } w.WriteVarBytes([]byte(aer.FaultException)) } diff --git a/pkg/core/state/notification_event_test.go b/pkg/core/state/notification_event_test.go index 2f6667f61..896e2b82a 100644 --- a/pkg/core/state/notification_event_test.go +++ b/pkg/core/state/notification_event_test.go @@ -13,6 +13,31 @@ import ( "github.com/stretchr/testify/require" ) +func BenchmarkAppExecResult_EncodeBinary(b *testing.B) { + aer := &AppExecResult{ + Container: random.Uint256(), + Execution: Execution{ + Trigger: trigger.Application, + VMState: vm.HaltState, + GasConsumed: 12345, + Stack: []stackitem.Item{}, + Events: []NotificationEvent{{ + ScriptHash: random.Uint160(), + Name: "Event", + Item: stackitem.NewArray([]stackitem.Item{stackitem.NewBool(true)}), + }}, + }, + } + + w := io.NewBufBinWriter() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + w.Reset() + aer.EncodeBinary(w.BinWriter) + } +} + func TestEncodeDecodeNotificationEvent(t *testing.T) { event := &NotificationEvent{ ScriptHash: random.Uint160(), diff --git a/pkg/core/transaction/bench_test.go b/pkg/core/transaction/bench_test.go index 1cf85a1cb..991d729dc 100644 --- a/pkg/core/transaction/bench_test.go +++ b/pkg/core/transaction/bench_test.go @@ -53,3 +53,14 @@ func BenchmarkDecodeFromBytes(t *testing.B) { require.NoError(t, err) } } + +func BenchmarkTransaction_Bytes(b *testing.B) { + tx, err := NewTransactionFromBytes(benchTx) + require.NoError(b, err) + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = tx.Bytes() + } +} diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 7dd9b8ccf..012ceb142 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -203,7 +203,10 @@ func (t *Transaction) DecodeBinary(br *io.BinReader) { // EncodeBinary implements Serializable interface. func (t *Transaction) EncodeBinary(bw *io.BinWriter) { t.encodeHashableFields(bw) - bw.WriteArray(t.Scripts) + bw.WriteVarUint(uint64(len(t.Scripts))) + for i := range t.Scripts { + t.Scripts[i].EncodeBinary(bw) + } } // encodeHashableFields encodes the fields that are not used for @@ -218,8 +221,14 @@ func (t *Transaction) encodeHashableFields(bw *io.BinWriter) { bw.WriteU64LE(uint64(t.SystemFee)) bw.WriteU64LE(uint64(t.NetworkFee)) bw.WriteU32LE(t.ValidUntilBlock) - bw.WriteArray(t.Signers) - bw.WriteArray(t.Attributes) + bw.WriteVarUint(uint64(len(t.Signers))) + for i := range t.Signers { + t.Signers[i].EncodeBinary(bw) + } + bw.WriteVarUint(uint64(len(t.Attributes))) + for i := range t.Attributes { + t.Attributes[i].EncodeBinary(bw) + } bw.WriteVarBytes(t.Script) }