From 9cf0f257c0627d2f2b4e741fcafa37bd93233730 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Tue, 25 Jul 2017 10:06:07 -0400 Subject: [PATCH] Add benchmark for abci app --- benchmarks/app_test.go | 199 +++++++++++++++++++++++++++++++++++++++++ modules/coin/helper.go | 9 +- 2 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 benchmarks/app_test.go diff --git a/benchmarks/app_test.go b/benchmarks/app_test.go new file mode 100644 index 000000000..03e188c3f --- /dev/null +++ b/benchmarks/app_test.go @@ -0,0 +1,199 @@ +package app + +import ( + "fmt" + "io/ioutil" + "testing" + + wire "github.com/tendermint/go-wire" + eyesApp "github.com/tendermint/merkleeyes/app" + eyes "github.com/tendermint/merkleeyes/client" + cmn "github.com/tendermint/tmlibs/common" + "github.com/tendermint/tmlibs/log" + + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/app" + "github.com/tendermint/basecoin/modules/auth" + "github.com/tendermint/basecoin/modules/base" + "github.com/tendermint/basecoin/modules/coin" + "github.com/tendermint/basecoin/modules/fee" + "github.com/tendermint/basecoin/modules/nonce" + "github.com/tendermint/basecoin/modules/roles" + "github.com/tendermint/basecoin/stack" +) + +type BenchApp struct { + App *app.Basecoin + Accounts []*coin.AccountWithKey + ChainID string +} + +// DefaultHandler - placeholder to just handle sendtx +func DefaultHandler(feeDenom string) basecoin.Handler { + // use the default stack + c := coin.NewHandler() + r := roles.NewHandler() + d := stack.NewDispatcher( + stack.WrapHandler(c), + stack.WrapHandler(r), + ) + return stack.New( + base.Logger{}, + stack.Recovery{}, + auth.Signatures{}, + base.Chain{}, + nonce.ReplayCheck{}, + roles.NewMiddleware(), + fee.NewSimpleFeeMiddleware(coin.Coin{feeDenom, 0}, fee.Bank), + ).Use(d) +} + +func NewBenchApp(h basecoin.Handler, chainID string, n int, + persist bool) BenchApp { + + logger := log.NewNopLogger() + // logger := log.NewFilter(log.NewTMLogger(os.Stdout), log.AllowError()) + // logger = log.NewTracingLogger(logger) + + // TODO: disk writing + var eyesCli *eyes.Client + if persist { + tmpDir, _ := ioutil.TempDir("", "bc-app-benchmark") + eyesCli = eyes.NewLocalClient(tmpDir, 500) + } else { + eyesCli = eyes.NewLocalClient("", 0) + } + eyesApp.SetLogger(logger.With("module", "merkle")) + + app := app.NewBasecoin( + h, + eyesCli, + logger.With("module", "app"), + ) + res := app.SetOption("base/chain_id", chainID) + if res != "Success" { + panic("cannot set chain") + } + + // make keys + money := coin.Coins{{"mycoin", 1234567890}} + accts := make([]*coin.AccountWithKey, n) + for i := 0; i < n; i++ { + accts[i] = coin.NewAccountWithKey(money) + res := app.SetOption("coin/account", accts[i].MakeOption()) + if res != "Success" { + panic("can't set account") + } + } + + return BenchApp{ + App: app, + Accounts: accts, + ChainID: chainID, + } +} + +// make a random tx... +func (b BenchApp) makeTx(useFee bool) []byte { + n := len(b.Accounts) + sender := b.Accounts[cmn.RandInt()%n] + recipient := b.Accounts[cmn.RandInt()%n] + amount := coin.Coins{{"mycoin", 123}} + tx := coin.NewSendOneTx(sender.Actor(), recipient.Actor(), amount) + if useFee { + toll := coin.Coin{"mycoin", 2} + tx = fee.NewFee(tx, toll, sender.Actor()) + } + sequence := sender.NextSequence() + tx = nonce.NewTx(sequence, []basecoin.Actor{sender.Actor()}, tx) + tx = base.NewChainTx(b.ChainID, 0, tx) + stx := auth.NewMulti(tx) + auth.Sign(stx, sender.Key) + res := wire.BinaryBytes(stx.Wrap()) + return res +} + +func BenchmarkMakeTx(b *testing.B) { + h := DefaultHandler("mycoin") + app := NewBenchApp(h, "bench-chain", 10, false) + b.ResetTimer() + for i := 1; i <= b.N; i++ { + txBytes := app.makeTx(true) + if len(txBytes) < 2 { + panic("cannot commit") + } + } +} + +func benchmarkTransfers(b *testing.B, app BenchApp, blockSize int, useFee bool) { + // prepare txs + txs := make([][]byte, b.N) + for i := 1; i <= b.N; i++ { + txBytes := app.makeTx(useFee) + if len(txBytes) < 2 { + panic("cannot make bytes") + } + txs[i-1] = txBytes + } + + b.ResetTimer() + + for i := 1; i <= b.N; i++ { + res := app.App.DeliverTx(txs[i-1]) + if res.IsErr() { + panic(res.Error()) + } + if i%blockSize == 0 { + res := app.App.Commit() + if res.IsErr() { + panic("cannot commit") + } + } + } +} + +func BenchmarkSimpleTransfer(b *testing.B) { + benchmarks := []struct { + accounts int + blockSize int + useFee bool + toDisk bool + }{ + {100, 10, false, false}, + {100, 10, true, false}, + {100, 200, false, false}, + {100, 200, true, false}, + {10000, 10, false, false}, + {10000, 10, true, false}, + {10000, 200, false, false}, + {10000, 200, true, false}, + {100, 10, false, true}, + {100, 10, true, true}, + {100, 200, false, true}, + {100, 200, true, true}, + {10000, 10, false, true}, + {10000, 10, true, true}, + {10000, 200, false, true}, + {10000, 200, true, true}, + } + + for _, bb := range benchmarks { + prefix := fmt.Sprintf("%d-%d", bb.accounts, bb.blockSize) + if bb.useFee { + prefix += "-fee" + } else { + prefix += "-nofee" + } + if bb.toDisk { + prefix += "-persist" + } else { + prefix += "-memdb" + } + + h := DefaultHandler("mycoin") + app := NewBenchApp(h, "bench-chain", bb.accounts, bb.toDisk) + b.Run(prefix, func(sub *testing.B) { + benchmarkTransfers(sub, app, bb.blockSize, bb.useFee) + }) + } +} diff --git a/modules/coin/helper.go b/modules/coin/helper.go index 4672e85d3..4bf98cddc 100644 --- a/modules/coin/helper.go +++ b/modules/coin/helper.go @@ -11,7 +11,8 @@ import ( // AccountWithKey is a helper for tests, that includes and account // along with the private key to access it. type AccountWithKey struct { - Key crypto.PrivKey + Key crypto.PrivKey + Sequence uint32 Account } @@ -34,6 +35,12 @@ func (a *AccountWithKey) Actor() basecoin.Actor { return auth.SigPerm(a.Key.PubKey().Address()) } +// NextSequence returns the next sequence to sign with +func (a *AccountWithKey) NextSequence() uint32 { + a.Sequence++ + return a.Sequence +} + // MakeOption returns a string to use with SetOption to initialize this account // // This is intended for use in test cases