Skip to content

Commit

Permalink
perf: benchmark end-to-end transaction submit
Browse files Browse the repository at this point in the history
The benchmark is lifted from E2ETestSuite from the same package, with
irrelevant state checks removed.

On my M1 macOS:

$ go test -run ^$ -bench=.
goos: darwin
goarch: arm64
pkg: github.com/cosmos/cosmos-sdk/tests/e2e/tx
BenchmarkTx
BenchmarkTx-8   	    4108	    268935 ns/op

Modifying the benchmark for v0.47.0:

$ go test -run ^$ -bench=.
goos: darwin
goarch: arm64
pkg: github.com/cosmos/cosmos-sdk/tests/e2e/tx
BenchmarkTx
BenchmarkTx-8   	    3772	    301750 ns/op

So a nice (~10%) speed-up in main.
  • Loading branch information
elias-orijtech committed Jul 10, 2023
1 parent 81b7049 commit 7153043
Showing 1 changed file with 191 additions and 0 deletions.
191 changes: 191 additions & 0 deletions tests/e2e/tx/benchmarks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package tx_test

import (
"context"
"fmt"
"testing"

"gotest.tools/v3/assert"

sdkmath "cosmossdk.io/math"
"cosmossdk.io/simapp"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
clienttx "github.com/cosmos/cosmos-sdk/client/tx"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)

type E2EBenchmarkSuite struct {
cfg network.Config
network *network.Network

txHeight int64
queryClient tx.ServiceClient
txRes sdk.TxResponse
}

// BenchmarkTx is lifted from E2ETestSuite from this package, with irrelevant state checks removed.
//
// Benchmark results:
//
// On a M1 macOS, on commit ab77fe20d3c00ef4cb73dfd0c803c4593a3c8233:
//
// BenchmarkTx-8 4108 268935 ns/op
//
// By comparison, the benchmark modified for v0.47.0:
//
// BenchmarkTx-8 3772 301750 ns/op
func BenchmarkTx(b *testing.B) {
s := NewE2EBenchmarkSuite(b)
b.Cleanup(s.Close)

val := s.network.Validators[0]
txBuilder := mkTxBuilder(b, s)
// Convert the txBuilder to a tx.Tx.
protoTx, err := txBuilderToProtoTx(txBuilder)
assert.NilError(b, err)
// Encode the txBuilder to txBytes.
txBytes, err := val.ClientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
assert.NilError(b, err)

testCases := []struct {
name string
req *tx.SimulateRequest
}{
{"valid request with proto tx (deprecated)", &tx.SimulateRequest{Tx: protoTx}},
{"valid request with tx_bytes", &tx.SimulateRequest{TxBytes: txBytes}},
}

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for _, tc := range testCases {
// Broadcast the tx via gRPC via the validator's clientCtx (which goes
// through Tendermint).
res, err := s.queryClient.Simulate(context.Background(), tc.req)
assert.NilError(b, err)
// Check the result and gas used are correct.
//
// The 12 events are:
// - Sending Fee to the pool: coin_spent, coin_received, transfer and message.sender=<val1>
// - tx.* events: tx.fee, tx.acc_seq, tx.signature
// - Sending Amount to recipient: coin_spent, coin_received, transfer and message.sender=<val1>
// - Msg events: message.module=bank and message.action=/cosmos.bank.v1beta1.MsgSend (in one message)
assert.Equal(b, 12, len(res.GetResult().GetEvents()))
assert.Assert(b, res.GetGasInfo().GetGasUsed() > 0) // Gas used sometimes change, just check it's not empty.
}
}
}

func NewE2EBenchmarkSuite(tb testing.TB) *E2EBenchmarkSuite {
tb.Helper()

s := new(E2EBenchmarkSuite)

cfg := network.DefaultConfig(simapp.NewTestNetworkFixture)
cfg.NumValidators = 1
s.cfg = cfg

var err error
s.network, err = network.New(tb, tb.TempDir(), s.cfg)
assert.NilError(tb, err)

val := s.network.Validators[0]
assert.NilError(tb, s.network.WaitForNextBlock())

s.queryClient = tx.NewServiceClient(val.ClientCtx)

// Create a new MsgSend tx from val to itself.
out, err := cli.MsgSendExec(
val.ClientCtx,
val.Address,
val.Address,
sdk.NewCoins(
sdk.NewCoin(s.cfg.BondDenom, sdkmath.NewInt(10)),
),
addresscodec.NewBech32Codec("cosmos"),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdkmath.NewInt(10))).String()),
fmt.Sprintf("--gas=%d", flags.DefaultGasLimit),
fmt.Sprintf("--%s=foobar", flags.FlagNote),
)
assert.NilError(tb, err)
assert.NilError(tb, val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &s.txRes))
assert.Equal(tb, uint32(0), s.txRes.Code, s.txRes)

out, err = cli.MsgSendExec(
val.ClientCtx,
val.Address,
val.Address,
sdk.NewCoins(
sdk.NewCoin(s.cfg.BondDenom, sdkmath.NewInt(1)),
),
addresscodec.NewBech32Codec("cosmos"),
fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation),
fmt.Sprintf("--%s", flags.FlagOffline),
fmt.Sprintf("--%s=0", flags.FlagAccountNumber),
fmt.Sprintf("--%s=2", flags.FlagSequence),
fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastSync),
fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdkmath.NewInt(10))).String()),
fmt.Sprintf("--gas=%d", flags.DefaultGasLimit),
fmt.Sprintf("--%s=foobar", flags.FlagNote),
)
assert.NilError(tb, err)
var tr sdk.TxResponse
assert.NilError(tb, val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &tr))
assert.Equal(tb, uint32(0), tr.Code)

resp, err := cli.GetTxResponse(s.network, val.ClientCtx, tr.TxHash)
assert.NilError(tb, err)
s.txHeight = resp.Height
return s
}

func (s *E2EBenchmarkSuite) Close() {
s.network.Cleanup()
}

func mkTxBuilder(tb testing.TB, s *E2EBenchmarkSuite) client.TxBuilder {
tb.Helper()

val := s.network.Validators[0]
assert.NilError(tb, s.network.WaitForNextBlock())

// prepare txBuilder with msg
txBuilder := val.ClientCtx.TxConfig.NewTxBuilder()
feeAmount := sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)}
gasLimit := testdata.NewTestGasLimit()
assert.NilError(tb,
txBuilder.SetMsgs(&banktypes.MsgSend{
FromAddress: val.Address.String(),
ToAddress: val.Address.String(),
Amount: sdk.Coins{sdk.NewInt64Coin(s.cfg.BondDenom, 10)},
}),
)
txBuilder.SetFeeAmount(feeAmount)
txBuilder.SetGasLimit(gasLimit)
txBuilder.SetMemo("foobar")

// setup txFactory
txFactory := clienttx.Factory{}.
WithChainID(val.ClientCtx.ChainID).
WithKeybase(val.ClientCtx.Keyring).
WithTxConfig(val.ClientCtx.TxConfig).
WithSignMode(signing.SignMode_SIGN_MODE_DIRECT)

// Sign Tx.
err := authclient.SignTx(txFactory, val.ClientCtx, val.Moniker, txBuilder, false, true)
assert.NilError(tb, err)

return txBuilder
}

0 comments on commit 7153043

Please sign in to comment.