mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
core/txpool/blobpool: reduce overhead in benchmark, make blobpool validation-function settable
The benchmarks for assembling the pending lazy-tx lists were extremely intense in setup, 1. writing a lot of data to disk, and 2. performing a lot of expensive blob validations. This change fixes both, by replacing billy with a no-op store, and replacing the blobpool validation function with a yes-dummy.
This commit is contained in:
parent
a4ab03cff8
commit
163e8f4b4d
3 changed files with 46 additions and 8 deletions
|
|
@ -318,6 +318,10 @@ type BlobPool struct {
|
||||||
discoverFeed event.Feed // Event feed to send out new tx events on pool discovery (reorg excluded)
|
discoverFeed event.Feed // Event feed to send out new tx events on pool discovery (reorg excluded)
|
||||||
insertFeed event.Feed // Event feed to send out new tx events on pool inclusion (reorg included)
|
insertFeed event.Feed // Event feed to send out new tx events on pool inclusion (reorg included)
|
||||||
|
|
||||||
|
// txValidationFn defaults to txpool.ValidateTransaction, but can be
|
||||||
|
// overridden for testing purposes.
|
||||||
|
txValidationFn txpool.ValidationFunction
|
||||||
|
|
||||||
lock sync.RWMutex // Mutex protecting the pool during reorg handling
|
lock sync.RWMutex // Mutex protecting the pool during reorg handling
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -329,12 +333,13 @@ func New(config Config, chain BlockChain) *BlobPool {
|
||||||
|
|
||||||
// Create the transaction pool with its initial settings
|
// Create the transaction pool with its initial settings
|
||||||
return &BlobPool{
|
return &BlobPool{
|
||||||
config: config,
|
config: config,
|
||||||
signer: types.LatestSigner(chain.Config()),
|
signer: types.LatestSigner(chain.Config()),
|
||||||
chain: chain,
|
chain: chain,
|
||||||
lookup: newLookup(),
|
lookup: newLookup(),
|
||||||
index: make(map[common.Address][]*blobTxMeta),
|
index: make(map[common.Address][]*blobTxMeta),
|
||||||
spent: make(map[common.Address]*uint256.Int),
|
spent: make(map[common.Address]*uint256.Int),
|
||||||
|
txValidationFn: txpool.ValidateTransaction,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1090,7 +1095,8 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error {
|
||||||
MaxSize: txMaxSize,
|
MaxSize: txMaxSize,
|
||||||
MinTip: p.gasTip.ToBig(),
|
MinTip: p.gasTip.ToBig(),
|
||||||
}
|
}
|
||||||
if err := txpool.ValidateTransaction(tx, p.head, p.signer, baseOpts); err != nil {
|
|
||||||
|
if err := p.txValidationFn(tx, p.head, p.signer, baseOpts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Ensure the transaction adheres to the stateful pool filters (nonce, balance)
|
// Ensure the transaction adheres to the stateful pool filters (nonce, balance)
|
||||||
|
|
|
||||||
|
|
@ -1449,11 +1449,29 @@ func TestAdd(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fakeBilly is a billy.Database implementation which just drops data on the floor.
|
||||||
|
type fakeBilly struct {
|
||||||
|
billy.Database
|
||||||
|
count uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeBilly) Put(data []byte) (uint64, error) {
|
||||||
|
f.count++
|
||||||
|
return f.count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ billy.Database = (*fakeBilly)(nil)
|
||||||
|
|
||||||
// Benchmarks the time it takes to assemble the lazy pending transaction list
|
// Benchmarks the time it takes to assemble the lazy pending transaction list
|
||||||
// from the pool contents.
|
// from the pool contents.
|
||||||
func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_000_000) }
|
func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_000_000) }
|
||||||
func BenchmarkPoolPending1GB(b *testing.B) { benchmarkPoolPending(b, 1_000_000_000) }
|
func BenchmarkPoolPending1GB(b *testing.B) { benchmarkPoolPending(b, 1_000_000_000) }
|
||||||
func BenchmarkPoolPending10GB(b *testing.B) { benchmarkPoolPending(b, 10_000_000_000) }
|
func BenchmarkPoolPending10GB(b *testing.B) {
|
||||||
|
if testing.Short() {
|
||||||
|
b.Skip("Skipping in short-mode")
|
||||||
|
}
|
||||||
|
benchmarkPoolPending(b, 10_000_000_000)
|
||||||
|
}
|
||||||
|
|
||||||
func benchmarkPoolPending(b *testing.B, datacap uint64) {
|
func benchmarkPoolPending(b *testing.B, datacap uint64) {
|
||||||
// Calculate the maximum number of transaction that would fit into the pool
|
// Calculate the maximum number of transaction that would fit into the pool
|
||||||
|
|
@ -1477,6 +1495,15 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) {
|
||||||
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
|
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
|
||||||
b.Fatalf("failed to create blob pool: %v", err)
|
b.Fatalf("failed to create blob pool: %v", err)
|
||||||
}
|
}
|
||||||
|
// Make the pool not use disk (just drop everything). This test never reads
|
||||||
|
// back the data, it just iterates over the pool in-memory items
|
||||||
|
pool.store = &fakeBilly{pool.store, 0}
|
||||||
|
// Avoid validation - verifying all blob proofs take significant time
|
||||||
|
// when the capacity is large. The purpose of this bench is to measure assembling
|
||||||
|
// the lazies, not the kzg verifications.
|
||||||
|
pool.txValidationFn = func(tx *types.Transaction, head *types.Header, signer types.Signer, opts *txpool.ValidationOptions) error {
|
||||||
|
return nil // accept all
|
||||||
|
}
|
||||||
// Fill the pool up with one random transaction from each account with the
|
// Fill the pool up with one random transaction from each account with the
|
||||||
// same price and everything to maximize the worst case scenario
|
// same price and everything to maximize the worst case scenario
|
||||||
for i := 0; i < int(capacity); i++ {
|
for i := 0; i < int(capacity); i++ {
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,11 @@ type ValidationOptions struct {
|
||||||
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
|
MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidationFunction is an method type which the pools use to perform the tx-validations which do not
|
||||||
|
// require state access. Production code typically uses ValidateTransaction, whereas testing-code
|
||||||
|
// might choose to instead use something else, e.g. to always fail or avoid heavy cpu usage.
|
||||||
|
type ValidationFunction func(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error
|
||||||
|
|
||||||
// ValidateTransaction is a helper method to check whether a transaction is valid
|
// ValidateTransaction is a helper method to check whether a transaction is valid
|
||||||
// according to the consensus rules, but does not check state-dependent validation
|
// according to the consensus rules, but does not check state-dependent validation
|
||||||
// (balance, nonce, etc).
|
// (balance, nonce, etc).
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue