-
Notifications
You must be signed in to change notification settings - Fork 856
Add a benchmark mode #2543
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
stevenlanders
wants to merge
18
commits into
main
Choose a base branch
from
steven/add-benchmark-mode
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add a benchmark mode #2543
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
39f3d49
add generator
stevenlanders 6635ecf
add ch
stevenlanders 30ac407
register proposal handler
stevenlanders 4a51fb9
rename benchmark
stevenlanders 5b29f8b
remove config based option because deploys won't work yet
stevenlanders 1226ce3
add benchmark scirpt
stevenlanders 662f2c0
fix base fee creeping
stevenlanders 250e16b
add check for safety
stevenlanders 9eb5967
use unmodified instead of generated
stevenlanders 29a0483
use build flag
stevenlanders 7d00ced
Merge branch 'main' into steven/add-benchmark-mode
stevenlanders b43e128
fix test
stevenlanders 7b98478
align previous expected error message
stevenlanders 93e34fb
fix genreated pb
stevenlanders ea0f50d
add defensive logic
stevenlanders 2ee1d29
undo changes
stevenlanders b1202a2
go fmt
stevenlanders e145c4f
remove watermark integration test
jewei1997 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,278 @@ | ||
| package app | ||
|
|
||
| import ( | ||
| "context" | ||
| "sync" | ||
| "time" | ||
|
|
||
| "github.com/cosmos/cosmos-sdk/client" | ||
| sdk "github.com/cosmos/cosmos-sdk/types" | ||
| evmcfg "github.com/sei-protocol/sei-chain/x/evm/config" | ||
| evmtypes "github.com/sei-protocol/sei-chain/x/evm/types" | ||
| "github.com/sei-protocol/sei-chain/x/evm/types/ethtx" | ||
| "github.com/sei-protocol/sei-load/config" | ||
| "github.com/sei-protocol/sei-load/generator" | ||
| "github.com/sei-protocol/sei-load/generator/scenarios" | ||
| abci "github.com/tendermint/tendermint/abci/types" | ||
| "github.com/tendermint/tendermint/libs/log" | ||
| ) | ||
|
|
||
| type benchmarkLogger struct { | ||
| mx sync.Mutex | ||
| txCount int64 // Total transactions processed | ||
| blockCount int64 // Number of times Increment was called (number of blocks) | ||
| latestHeight int64 // Highest height seen in the window | ||
| maxBlockTime time.Duration // Maximum time difference between consecutive blocks | ||
| totalBlockTime time.Duration // Sum of all block time differences in the window | ||
| blockTimeCount int64 // Number of block time differences calculated | ||
| peakTps float64 // Highest TPS seen across entire execution (persists across flushes) | ||
| prevBlockTime time.Time // Previous block time for calculating differences | ||
| lastFlushTime time.Time // When we last flushed (for TPS calculation) | ||
| logger log.Logger | ||
| } | ||
|
|
||
| func (l *benchmarkLogger) Increment(count int64, blocktime time.Time, height int64) { | ||
| l.mx.Lock() | ||
| defer l.mx.Unlock() | ||
|
|
||
| // Initialize lastFlushTime on first increment (when blocks actually start processing) | ||
| if l.lastFlushTime.IsZero() { | ||
| l.lastFlushTime = time.Now() | ||
| } | ||
|
|
||
| l.txCount += count | ||
| l.blockCount++ | ||
| if height > l.latestHeight { | ||
| l.latestHeight = height | ||
| } | ||
|
|
||
| // Calculate time difference between consecutive blocks | ||
| if !l.prevBlockTime.IsZero() { | ||
| blockTimeDiff := blocktime.Sub(l.prevBlockTime) | ||
| if blockTimeDiff > l.maxBlockTime { | ||
| l.maxBlockTime = blockTimeDiff | ||
| } | ||
| l.totalBlockTime += blockTimeDiff | ||
| l.blockTimeCount++ | ||
| } | ||
| l.prevBlockTime = blocktime | ||
| } | ||
|
|
||
| // calculateTPS computes transactions per second based on transaction count and duration | ||
| func calculateTPS(txCount int64, duration time.Duration) float64 { | ||
| if duration <= 0 { | ||
| return 0 | ||
| } | ||
| return float64(txCount) / duration.Seconds() | ||
|
||
| } | ||
|
|
||
| // calculateAvgBlockTime computes the average block time from total block time and count | ||
| func calculateAvgBlockTime(totalBlockTime time.Duration, blockTimeCount int64) int64 { | ||
| if blockTimeCount <= 0 { | ||
| return 0 | ||
| } | ||
| avgBlockTime := totalBlockTime / time.Duration(blockTimeCount) | ||
| return avgBlockTime.Milliseconds() | ||
| } | ||
|
|
||
| // flushStats holds the statistics for a flush window | ||
| type flushStats struct { | ||
| txCount int64 | ||
| blockCount int64 | ||
| latestHeight int64 | ||
| maxBlockTimeMs int64 | ||
| avgBlockTimeMs int64 | ||
| tps float64 | ||
| peakTps float64 | ||
| } | ||
|
|
||
| // getAndResetStats atomically reads current stats and resets counters for next window | ||
| func (l *benchmarkLogger) getAndResetStats(now time.Time) (flushStats, time.Time) { | ||
| l.mx.Lock() | ||
| defer l.mx.Unlock() | ||
|
|
||
| stats := flushStats{ | ||
| txCount: l.txCount, | ||
| blockCount: l.blockCount, | ||
| latestHeight: l.latestHeight, | ||
| maxBlockTimeMs: l.maxBlockTime.Milliseconds(), | ||
| } | ||
|
|
||
| prevTime := l.lastFlushTime | ||
| totalBlockTime := l.totalBlockTime | ||
| blockTimeCount := l.blockTimeCount | ||
|
|
||
| // Reset counters for next window (but keep prevBlockTime and peakTps for continuity) | ||
| l.txCount = 0 | ||
| l.blockCount = 0 | ||
| l.latestHeight = 0 | ||
| l.maxBlockTime = 0 | ||
| l.totalBlockTime = 0 | ||
| l.blockTimeCount = 0 | ||
| l.lastFlushTime = now | ||
|
|
||
| // Calculate TPS | ||
| duration := now.Sub(prevTime) | ||
| if duration > 0 && !prevTime.IsZero() { | ||
| stats.tps = calculateTPS(stats.txCount, duration) | ||
| } | ||
|
|
||
| // Calculate average block time | ||
| stats.avgBlockTimeMs = calculateAvgBlockTime(totalBlockTime, blockTimeCount) | ||
|
|
||
| // Update peak TPS if current TPS is higher | ||
| if stats.tps > l.peakTps { | ||
| l.peakTps = stats.tps | ||
| } | ||
| stats.peakTps = l.peakTps | ||
|
|
||
| return stats, prevTime | ||
| } | ||
|
|
||
| func (l *benchmarkLogger) FlushLog() { | ||
| now := time.Now() | ||
|
||
| stats, _ := l.getAndResetStats(now) | ||
|
|
||
| l.logger.Info("benchmark", | ||
| "txs", stats.txCount, | ||
| "blocks", stats.blockCount, | ||
| "height", stats.latestHeight, | ||
| "blockTimeMax", stats.maxBlockTimeMs, | ||
| "blockTimeAvg", stats.avgBlockTimeMs, | ||
| "tps", stats.tps, | ||
| "peakTps", stats.peakTps, | ||
| ) | ||
| } | ||
|
|
||
| func (l *benchmarkLogger) Start(ctx context.Context) { | ||
| ticker := time.NewTicker(5 * time.Second) | ||
| defer ticker.Stop() | ||
| for { | ||
| select { | ||
| case <-ctx.Done(): | ||
| return | ||
| case <-ticker.C: | ||
| l.FlushLog() | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func NewGeneratorCh(ctx context.Context, txConfig client.TxConfig, chainID string, evmChainID int64, logger log.Logger) <-chan *abci.ResponsePrepareProposal { | ||
| gen, err := generator.NewConfigBasedGenerator(&config.LoadConfig{ | ||
| ChainID: evmChainID, | ||
| SeiChainID: chainID, | ||
| Accounts: &config.AccountConfig{Accounts: 5000}, | ||
| Scenarios: []config.Scenario{{ | ||
| Name: scenarios.EVMTransfer, | ||
| Weight: 1, | ||
| }}, | ||
| }) | ||
| if err != nil { | ||
| panic("failed to initialize generator: " + err.Error()) | ||
| } | ||
| ch := make(chan *abci.ResponsePrepareProposal, 100) | ||
| go func() { | ||
| defer close(ch) | ||
| var height int64 | ||
| for { | ||
| // bail on ctx err | ||
| if ctx.Err() != nil { | ||
| return | ||
| } | ||
| // generate txs like: txs := gen.GenerateN(1000) | ||
| loadTxs := gen.GenerateN(1000) | ||
| if len(loadTxs) == 0 { | ||
| continue | ||
| } | ||
|
|
||
| // Convert LoadTx to Cosmos SDK transaction bytes | ||
| txRecords := make([]*abci.TxRecord, 0, len(loadTxs)) | ||
| for _, loadTx := range loadTxs { | ||
| if loadTx.EthTx == nil { | ||
| continue | ||
| } | ||
|
|
||
| // Convert Ethereum transaction to Cosmos SDK format | ||
| txData, err := ethtx.NewTxDataFromTx(loadTx.EthTx) | ||
| if err != nil { | ||
| logger.Error("failed to convert eth tx to tx data", "error", err) | ||
| panic(err) | ||
| } | ||
|
|
||
| msg, err := evmtypes.NewMsgEVMTransaction(txData) | ||
| if err != nil { | ||
| logger.Error("failed to create msg evm transaction", "error", err) | ||
| panic(err) | ||
| } | ||
|
|
||
| gasUsedEstimate := loadTx.EthTx.Gas() // Use gas limit from transaction | ||
|
|
||
| txBuilder := txConfig.NewTxBuilder() | ||
| if err = txBuilder.SetMsgs(msg); err != nil { | ||
| logger.Error("failed to set msgs", "error", err) | ||
| panic(err) | ||
| } | ||
| txBuilder.SetGasEstimate(gasUsedEstimate) | ||
|
|
||
| txbz, encodeErr := txConfig.TxEncoder()(txBuilder.GetTx()) | ||
| if encodeErr != nil { | ||
| logger.Error("failed to encode tx", "error", encodeErr) | ||
| panic(encodeErr) | ||
| } | ||
|
|
||
| txRecords = append(txRecords, &abci.TxRecord{ | ||
| Action: abci.TxRecord_UNMODIFIED, | ||
| Tx: txbz, | ||
| }) | ||
| } | ||
|
|
||
| if len(txRecords) == 0 { | ||
| continue | ||
| } | ||
|
|
||
| proposal := &abci.ResponsePrepareProposal{ | ||
| TxRecords: txRecords, | ||
| } | ||
|
|
||
| height++ | ||
| select { | ||
| case ch <- proposal: | ||
| case <-ctx.Done(): | ||
| return | ||
| } | ||
| } | ||
| }() | ||
| return ch | ||
| } | ||
|
|
||
| // InitGenerator initializes the benchmark generator with default config | ||
| func (app *App) InitGenerator(ctx context.Context, chainID string, evmChainID int64, logger log.Logger) { | ||
| // defensive logic just to prevent this from initializing | ||
| if evmcfg.IsLiveEVMChainID(evmChainID) { | ||
| panic("benchmark not allowed on live chains") | ||
| } | ||
| logger.Info("Initializing benchmark mode generator", "mode", "benchmark") | ||
| app.benchmarkLogger = &benchmarkLogger{ | ||
| logger: logger, | ||
| } | ||
| go app.benchmarkLogger.Start(ctx) | ||
|
||
| app.benchmarkProposalCh = NewGeneratorCh(ctx, app.encodingConfig.TxConfig, chainID, evmChainID, logger) | ||
| logger.Info("Benchmark generator initialized and started", "config", "default EVM Transfers") | ||
| } | ||
|
|
||
| func (app *App) PrepareProposalGeneratorHandler(_ sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { | ||
| select { | ||
| case proposal, ok := <-app.benchmarkProposalCh: | ||
| if proposal == nil || !ok { | ||
| return &abci.ResponsePrepareProposal{ | ||
| TxRecords: []*abci.TxRecord{}, | ||
| }, nil | ||
| } | ||
| app.benchmarkLogger.Increment(int64(len(proposal.TxRecords)), req.Time, req.Height) | ||
| return proposal, nil | ||
| default: | ||
| return &abci.ResponsePrepareProposal{ | ||
| TxRecords: []*abci.TxRecord{}, | ||
| }, nil | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| //go:build benchmark | ||
|
|
||
| package app | ||
|
|
||
| // benchmarkEnabled is set to true when built with benchmark build tag | ||
| const benchmarkEnabled = true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| //go:build !benchmark | ||
|
|
||
| package app | ||
|
|
||
| // benchmarkEnabled is set to false when not built with benchmark build tag | ||
| const benchmarkEnabled = false |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.