Skip to content

Commit 6fb0d09

Browse files
karalabeholiman
andauthored
core/txpool, miner: speed up blob pool pending retrievals (#29008)
* core/txpool, miner: speed up blob pool pending retrievals * miner: fix test merge issue * eth: same same * core/txpool/blobpool: speed up blobtx creation in benchmark a bit * core/txpool/blobpool: fix linter --------- Co-authored-by: Martin Holst Swende <martin@swende.se>
1 parent 5d98479 commit 6fb0d09

File tree

8 files changed

+105
-37
lines changed

8 files changed

+105
-37
lines changed

core/txpool/blobpool/blobpool.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -1456,13 +1456,14 @@ func (p *BlobPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee *u
14561456
pendwaitHist.Update(time.Since(pendStart).Nanoseconds())
14571457
defer p.lock.RUnlock()
14581458

1459-
defer func(start time.Time) {
1460-
pendtimeHist.Update(time.Since(start).Nanoseconds())
1461-
}(time.Now())
1459+
execStart := time.Now()
1460+
defer func() {
1461+
pendtimeHist.Update(time.Since(execStart).Nanoseconds())
1462+
}()
14621463

1463-
pending := make(map[common.Address][]*txpool.LazyTransaction)
1464+
pending := make(map[common.Address][]*txpool.LazyTransaction, len(p.index))
14641465
for addr, txs := range p.index {
1465-
var lazies []*txpool.LazyTransaction
1466+
lazies := make([]*txpool.LazyTransaction, 0, len(txs))
14661467
for _, tx := range txs {
14671468
// If transaction filtering was requested, discard badly priced ones
14681469
if minTip != nil && baseFee != nil {
@@ -1486,9 +1487,9 @@ func (p *BlobPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee *u
14861487
lazies = append(lazies, &txpool.LazyTransaction{
14871488
Pool: p,
14881489
Hash: tx.hash,
1489-
Time: time.Now(), // TODO(karalabe): Maybe save these and use that?
1490-
GasFeeCap: tx.execFeeCap.ToBig(),
1491-
GasTipCap: tx.execTipCap.ToBig(),
1490+
Time: execStart, // TODO(karalabe): Maybe save these and use that?
1491+
GasFeeCap: tx.execFeeCap,
1492+
GasTipCap: tx.execTipCap,
14921493
Gas: tx.execGas,
14931494
BlobGas: tx.blobGas,
14941495
})

core/txpool/blobpool/blobpool_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -1288,3 +1288,61 @@ func TestAdd(t *testing.T) {
12881288
pool.Close()
12891289
}
12901290
}
1291+
1292+
// Benchmarks the time it takes to assemble the lazy pending transaction list
1293+
// from the pool contents.
1294+
func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_000_000) }
1295+
func BenchmarkPoolPending1GB(b *testing.B) { benchmarkPoolPending(b, 1_000_000_000) }
1296+
func BenchmarkPoolPending10GB(b *testing.B) { benchmarkPoolPending(b, 10_000_000_000) }
1297+
1298+
func benchmarkPoolPending(b *testing.B, datacap uint64) {
1299+
// Calculate the maximum number of transaction that would fit into the pool
1300+
// and generate a set of random accounts to seed them with.
1301+
capacity := datacap / params.BlobTxBlobGasPerBlob
1302+
1303+
var (
1304+
basefee = uint64(1050)
1305+
blobfee = uint64(105)
1306+
signer = types.LatestSigner(testChainConfig)
1307+
statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
1308+
chain = &testBlockChain{
1309+
config: testChainConfig,
1310+
basefee: uint256.NewInt(basefee),
1311+
blobfee: uint256.NewInt(blobfee),
1312+
statedb: statedb,
1313+
}
1314+
pool = New(Config{Datadir: ""}, chain)
1315+
)
1316+
1317+
if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
1318+
b.Fatalf("failed to create blob pool: %v", err)
1319+
}
1320+
// Fill the pool up with one random transaction from each account with the
1321+
// same price and everything to maximize the worst case scenario
1322+
for i := 0; i < int(capacity); i++ {
1323+
blobtx := makeUnsignedTx(0, 10, basefee+10, blobfee)
1324+
blobtx.R = uint256.NewInt(1)
1325+
blobtx.S = uint256.NewInt(uint64(100 + i))
1326+
blobtx.V = uint256.NewInt(0)
1327+
tx := types.NewTx(blobtx)
1328+
addr, err := types.Sender(signer, tx)
1329+
if err != nil {
1330+
b.Fatal(err)
1331+
}
1332+
statedb.AddBalance(addr, uint256.NewInt(1_000_000_000))
1333+
pool.add(tx)
1334+
}
1335+
statedb.Commit(0, true)
1336+
defer pool.Close()
1337+
1338+
// Benchmark assembling the pending
1339+
b.ResetTimer()
1340+
b.ReportAllocs()
1341+
1342+
for i := 0; i < b.N; i++ {
1343+
p := pool.Pending(uint256.NewInt(1), chain.basefee, chain.blobfee)
1344+
if len(p) != int(capacity) {
1345+
b.Fatalf("have %d want %d", len(p), capacity)
1346+
}
1347+
}
1348+
}

core/txpool/legacypool/legacypool.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -559,8 +559,8 @@ func (pool *LegacyPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobF
559559
Hash: txs[i].Hash(),
560560
Tx: txs[i],
561561
Time: txs[i].Time(),
562-
GasFeeCap: txs[i].GasFeeCap(),
563-
GasTipCap: txs[i].GasTipCap(),
562+
GasFeeCap: uint256.MustFromBig(txs[i].GasFeeCap()),
563+
GasTipCap: uint256.MustFromBig(txs[i].GasTipCap()),
564564
Gas: txs[i].Gas(),
565565
BlobGas: txs[i].BlobGas(),
566566
}

core/txpool/subpool.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ type LazyTransaction struct {
3535
Hash common.Hash // Transaction hash to pull up if needed
3636
Tx *types.Transaction // Transaction if already resolved
3737

38-
Time time.Time // Time when the transaction was first seen
39-
GasFeeCap *big.Int // Maximum fee per gas the transaction may consume
40-
GasTipCap *big.Int // Maximum miner tip per gas the transaction can pay
38+
Time time.Time // Time when the transaction was first seen
39+
GasFeeCap *uint256.Int // Maximum fee per gas the transaction may consume
40+
GasTipCap *uint256.Int // Maximum miner tip per gas the transaction can pay
4141

4242
Gas uint64 // Amount of gas required by the transaction
4343
BlobGas uint64 // Amount of blob gas required by the transaction

eth/handler_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,8 @@ func (p *testTxPool) Pending(minTip *uint256.Int, baseFee *uint256.Int, blobFee
112112
Hash: tx.Hash(),
113113
Tx: tx,
114114
Time: tx.Time(),
115-
GasFeeCap: tx.GasFeeCap(),
116-
GasTipCap: tx.GasTipCap(),
115+
GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
116+
GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
117117
Gas: tx.Gas(),
118118
BlobGas: tx.BlobGas(),
119119
})

miner/ordering.go

+17-9
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,31 @@ import (
2121
"math/big"
2222

2323
"github.com/ethereum/go-ethereum/common"
24-
"github.com/ethereum/go-ethereum/common/math"
2524
"github.com/ethereum/go-ethereum/core/txpool"
2625
"github.com/ethereum/go-ethereum/core/types"
26+
"github.com/holiman/uint256"
2727
)
2828

2929
// txWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap
3030
type txWithMinerFee struct {
3131
tx *txpool.LazyTransaction
3232
from common.Address
33-
fees *big.Int
33+
fees *uint256.Int
3434
}
3535

3636
// newTxWithMinerFee creates a wrapped transaction, calculating the effective
3737
// miner gasTipCap if a base fee is provided.
3838
// Returns error in case of a negative effective miner gasTipCap.
39-
func newTxWithMinerFee(tx *txpool.LazyTransaction, from common.Address, baseFee *big.Int) (*txWithMinerFee, error) {
40-
tip := new(big.Int).Set(tx.GasTipCap)
39+
func newTxWithMinerFee(tx *txpool.LazyTransaction, from common.Address, baseFee *uint256.Int) (*txWithMinerFee, error) {
40+
tip := new(uint256.Int).Set(tx.GasTipCap)
4141
if baseFee != nil {
4242
if tx.GasFeeCap.Cmp(baseFee) < 0 {
4343
return nil, types.ErrGasFeeCapTooLow
4444
}
45-
tip = math.BigMin(tx.GasTipCap, new(big.Int).Sub(tx.GasFeeCap, baseFee))
45+
tip = new(uint256.Int).Sub(tx.GasFeeCap, baseFee)
46+
if tip.Gt(tx.GasTipCap) {
47+
tip = tx.GasTipCap
48+
}
4649
}
4750
return &txWithMinerFee{
4851
tx: tx,
@@ -87,7 +90,7 @@ type transactionsByPriceAndNonce struct {
8790
txs map[common.Address][]*txpool.LazyTransaction // Per account nonce-sorted list of transactions
8891
heads txByPriceAndTime // Next transaction for each unique account (price heap)
8992
signer types.Signer // Signer for the set of transactions
90-
baseFee *big.Int // Current base fee
93+
baseFee *uint256.Int // Current base fee
9194
}
9295

9396
// newTransactionsByPriceAndNonce creates a transaction set that can retrieve
@@ -96,10 +99,15 @@ type transactionsByPriceAndNonce struct {
9699
// Note, the input map is reowned so the caller should not interact any more with
97100
// if after providing it to the constructor.
98101
func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address][]*txpool.LazyTransaction, baseFee *big.Int) *transactionsByPriceAndNonce {
102+
// Convert the basefee from header format to uint256 format
103+
var baseFeeUint *uint256.Int
104+
if baseFee != nil {
105+
baseFeeUint = uint256.MustFromBig(baseFee)
106+
}
99107
// Initialize a price and received time based heap with the head transactions
100108
heads := make(txByPriceAndTime, 0, len(txs))
101109
for from, accTxs := range txs {
102-
wrapped, err := newTxWithMinerFee(accTxs[0], from, baseFee)
110+
wrapped, err := newTxWithMinerFee(accTxs[0], from, baseFeeUint)
103111
if err != nil {
104112
delete(txs, from)
105113
continue
@@ -114,12 +122,12 @@ func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address]
114122
txs: txs,
115123
heads: heads,
116124
signer: signer,
117-
baseFee: baseFee,
125+
baseFee: baseFeeUint,
118126
}
119127
}
120128

121129
// Peek returns the next transaction by price.
122-
func (t *transactionsByPriceAndNonce) Peek() (*txpool.LazyTransaction, *big.Int) {
130+
func (t *transactionsByPriceAndNonce) Peek() (*txpool.LazyTransaction, *uint256.Int) {
123131
if len(t.heads) == 0 {
124132
return nil, nil
125133
}

miner/ordering_test.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/ethereum/go-ethereum/core/txpool"
2828
"github.com/ethereum/go-ethereum/core/types"
2929
"github.com/ethereum/go-ethereum/crypto"
30+
"github.com/holiman/uint256"
3031
)
3132

3233
func TestTransactionPriceNonceSortLegacy(t *testing.T) {
@@ -92,8 +93,8 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
9293
Hash: tx.Hash(),
9394
Tx: tx,
9495
Time: tx.Time(),
95-
GasFeeCap: tx.GasFeeCap(),
96-
GasTipCap: tx.GasTipCap(),
96+
GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
97+
GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
9798
Gas: tx.Gas(),
9899
BlobGas: tx.BlobGas(),
99100
})
@@ -160,8 +161,8 @@ func TestTransactionTimeSort(t *testing.T) {
160161
Hash: tx.Hash(),
161162
Tx: tx,
162163
Time: tx.Time(),
163-
GasFeeCap: tx.GasFeeCap(),
164-
GasTipCap: tx.GasTipCap(),
164+
GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
165+
GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
165166
Gas: tx.Gas(),
166167
BlobGas: tx.BlobGas(),
167168
})

miner/worker.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ type worker struct {
206206
mu sync.RWMutex // The lock used to protect the coinbase and extra fields
207207
coinbase common.Address
208208
extra []byte
209-
tip *big.Int // Minimum tip needed for non-local transaction to include them
209+
tip *uint256.Int // Minimum tip needed for non-local transaction to include them
210210

211211
pendingMu sync.RWMutex
212212
pendingTasks map[common.Hash]*task
@@ -253,7 +253,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
253253
isLocalBlock: isLocalBlock,
254254
coinbase: config.Etherbase,
255255
extra: config.ExtraData,
256-
tip: config.GasPrice,
256+
tip: uint256.MustFromBig(config.GasPrice),
257257
pendingTasks: make(map[common.Hash]*task),
258258
txsCh: make(chan core.NewTxsEvent, txChanSize),
259259
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
@@ -334,7 +334,7 @@ func (w *worker) setExtra(extra []byte) {
334334
func (w *worker) setGasTip(tip *big.Int) {
335335
w.mu.Lock()
336336
defer w.mu.Unlock()
337-
w.tip = tip
337+
w.tip = uint256.MustFromBig(tip)
338338
}
339339

340340
// setRecommitInterval updates the interval for miner sealing work recommitting.
@@ -556,15 +556,15 @@ func (w *worker) mainLoop() {
556556
Hash: tx.Hash(),
557557
Tx: nil, // Do *not* set this! We need to resolve it later to pull blobs in
558558
Time: tx.Time(),
559-
GasFeeCap: tx.GasFeeCap(),
560-
GasTipCap: tx.GasTipCap(),
559+
GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
560+
GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
561561
Gas: tx.Gas(),
562562
BlobGas: tx.BlobGas(),
563563
})
564564
}
565565
txset := newTransactionsByPriceAndNonce(w.current.signer, txs, w.current.header.BaseFee)
566566
tcount := w.current.tcount
567-
w.commitTransactions(w.current, txset, nil, new(big.Int))
567+
w.commitTransactions(w.current, txset, nil, new(uint256.Int))
568568

569569
// Only update the snapshot if any new transactions were added
570570
// to the pending block
@@ -802,7 +802,7 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction) (*typ
802802
return receipt, err
803803
}
804804

805-
func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, interrupt *atomic.Int32, minTip *big.Int) error {
805+
func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, interrupt *atomic.Int32, minTip *uint256.Int) error {
806806
gasLimit := env.header.GasLimit
807807
if env.gasPool == nil {
808808
env.gasPool = new(core.GasPool).AddGas(gasLimit)
@@ -1013,7 +1013,7 @@ func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) err
10131013
if env.header.ExcessBlobGas != nil {
10141014
blobFee = uint256.MustFromBig(eip4844.CalcBlobFee(*env.header.ExcessBlobGas))
10151015
}
1016-
pending := w.eth.TxPool().Pending(uint256.MustFromBig(tip), baseFee, blobFee)
1016+
pending := w.eth.TxPool().Pending(tip, baseFee, blobFee)
10171017

10181018
// Split the pending transactions into locals and remotes.
10191019
localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pending
@@ -1027,7 +1027,7 @@ func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) err
10271027
// Fill the block with all available pending transactions.
10281028
if len(localTxs) > 0 {
10291029
txs := newTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
1030-
if err := w.commitTransactions(env, txs, interrupt, new(big.Int)); err != nil {
1030+
if err := w.commitTransactions(env, txs, interrupt, new(uint256.Int)); err != nil {
10311031
return err
10321032
}
10331033
}

0 commit comments

Comments
 (0)