Skip to content

Commit

Permalink
core/txpool/blobpool: reduce overhead in benchmark, make blobpool val…
Browse files Browse the repository at this point in the history
…idation-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.
  • Loading branch information
holiman committed Oct 25, 2024
1 parent 393d8b7 commit f1da70f
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 9 deletions.
20 changes: 13 additions & 7 deletions core/txpool/blobpool/blobpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ type BlobPool struct {
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)

// 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
}

Expand All @@ -329,12 +333,13 @@ func New(config Config, chain BlockChain) *BlobPool {

// Create the transaction pool with its initial settings
return &BlobPool{
config: config,
signer: types.LatestSigner(chain.Config()),
chain: chain,
lookup: newLookup(),
index: make(map[common.Address][]*blobTxMeta),
spent: make(map[common.Address]*uint256.Int),
config: config,
signer: types.LatestSigner(chain.Config()),
chain: chain,
lookup: newLookup(),
index: make(map[common.Address][]*blobTxMeta),
spent: make(map[common.Address]*uint256.Int),
txValidationFn: txpool.ValidateTransaction,
}
}

Expand Down Expand Up @@ -1090,7 +1095,8 @@ func (p *BlobPool) validateTx(tx *types.Transaction) error {
MaxSize: txMaxSize,
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
}
// Ensure the transaction adheres to the stateful pool filters (nonce, balance)
Expand Down
31 changes: 29 additions & 2 deletions core/txpool/blobpool/blobpool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
// from the pool contents.
func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_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) {
// Calculate the maximum number of transaction that would fit into the pool
Expand All @@ -1477,13 +1495,22 @@ func benchmarkPoolPending(b *testing.B, datacap uint64) {
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
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
// same price and everything to maximize the worst case scenario
for i := 0; i < int(capacity); i++ {
blobtx := makeUnsignedTx(0, 10, basefee+10, blobfee)
blobtx.R = uint256.NewInt(1)
blobtx.S = uint256.NewInt(uint64(100 + i))
blobtx.V = uint256.NewInt(0)
blobtx.V = fuint256.NewInt(0)

Check failure on line 1513 in core/txpool/blobpool/blobpool_test.go

View workflow job for this annotation

GitHub Actions / build

undefined: fuint256
tx := types.NewTx(blobtx)
addr, err := types.Sender(signer, tx)
if err != nil {
Expand Down
5 changes: 5 additions & 0 deletions core/txpool/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ type ValidationOptions struct {
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
// according to the consensus rules, but does not check state-dependent validation
// (balance, nonce, etc).
Expand Down

0 comments on commit f1da70f

Please sign in to comment.