core, txpool: less allocations when handling transactions (#21232)
* core: use uint64 for total tx costs instead of big.Int * core: added local tx pool test case * core, crypto: various allocation savings regarding tx handling * Update core/tx_list.go * core: added tx.GasPriceIntCmp for comparison without allocation adds a method to remove unneeded allocation in comparison to tx.gasPrice * core: handle pools full of locals better * core/tests: benchmark for tx_list * core/txlist, txpool: save a reheap operation, avoid some bigint allocs Co-authored-by: Martin Holst Swende <martin@swende.se>
This commit is contained in:
parent
8dfd66f701
commit
af5c97aebe
|
@ -18,6 +18,7 @@ package math
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/bits"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -87,13 +88,12 @@ func SafeSub(x, y uint64) (uint64, bool) {
|
||||||
|
|
||||||
// SafeAdd returns the result and whether overflow occurred.
|
// SafeAdd returns the result and whether overflow occurred.
|
||||||
func SafeAdd(x, y uint64) (uint64, bool) {
|
func SafeAdd(x, y uint64) (uint64, bool) {
|
||||||
return x + y, y > MaxUint64-x
|
sum, carry := bits.Add64(x, y, 0)
|
||||||
|
return sum, carry != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SafeMul returns multiplication result and whether overflow occurred.
|
// SafeMul returns multiplication result and whether overflow occurred.
|
||||||
func SafeMul(x, y uint64) (uint64, bool) {
|
func SafeMul(x, y uint64) (uint64, bool) {
|
||||||
if x == 0 || y == 0 {
|
hi, lo := bits.Mul64(x, y)
|
||||||
return 0, false
|
return lo, hi != 0
|
||||||
}
|
|
||||||
return x * y, y > MaxUint64/x
|
|
||||||
}
|
}
|
||||||
|
|
121
core/tx_list.go
121
core/tx_list.go
|
@ -99,7 +99,30 @@ func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
|
||||||
|
|
||||||
// Filter iterates over the list of transactions and removes all of them for which
|
// Filter iterates over the list of transactions and removes all of them for which
|
||||||
// the specified function evaluates to true.
|
// the specified function evaluates to true.
|
||||||
|
// Filter, as opposed to 'filter', re-initialises the heap after the operation is done.
|
||||||
|
// If you want to do several consecutive filterings, it's therefore better to first
|
||||||
|
// do a .filter(func1) followed by .Filter(func2) or reheap()
|
||||||
func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
|
func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
|
||||||
|
removed := m.filter(filter)
|
||||||
|
// If transactions were removed, the heap and cache are ruined
|
||||||
|
if len(removed) > 0 {
|
||||||
|
m.reheap()
|
||||||
|
}
|
||||||
|
return removed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *txSortedMap) reheap() {
|
||||||
|
*m.index = make([]uint64, 0, len(m.items))
|
||||||
|
for nonce := range m.items {
|
||||||
|
*m.index = append(*m.index, nonce)
|
||||||
|
}
|
||||||
|
heap.Init(m.index)
|
||||||
|
m.cache = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter is identical to Filter, but **does not** regenerate the heap. This method
|
||||||
|
// should only be used if followed immediately by a call to Filter or reheap()
|
||||||
|
func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transactions {
|
||||||
var removed types.Transactions
|
var removed types.Transactions
|
||||||
|
|
||||||
// Collect all the transactions to filter out
|
// Collect all the transactions to filter out
|
||||||
|
@ -109,14 +132,7 @@ func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transac
|
||||||
delete(m.items, nonce)
|
delete(m.items, nonce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If transactions were removed, the heap and cache are ruined
|
|
||||||
if len(removed) > 0 {
|
if len(removed) > 0 {
|
||||||
*m.index = make([]uint64, 0, len(m.items))
|
|
||||||
for nonce := range m.items {
|
|
||||||
*m.index = append(*m.index, nonce)
|
|
||||||
}
|
|
||||||
heap.Init(m.index)
|
|
||||||
|
|
||||||
m.cache = nil
|
m.cache = nil
|
||||||
}
|
}
|
||||||
return removed
|
return removed
|
||||||
|
@ -197,10 +213,7 @@ func (m *txSortedMap) Len() int {
|
||||||
return len(m.items)
|
return len(m.items)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
func (m *txSortedMap) flatten() types.Transactions {
|
||||||
// sorted internal representation. The result of the sorting is cached in case
|
|
||||||
// it's requested again before any modifications are made to the contents.
|
|
||||||
func (m *txSortedMap) Flatten() types.Transactions {
|
|
||||||
// If the sorting was not cached yet, create and cache it
|
// If the sorting was not cached yet, create and cache it
|
||||||
if m.cache == nil {
|
if m.cache == nil {
|
||||||
m.cache = make(types.Transactions, 0, len(m.items))
|
m.cache = make(types.Transactions, 0, len(m.items))
|
||||||
|
@ -209,12 +222,27 @@ func (m *txSortedMap) Flatten() types.Transactions {
|
||||||
}
|
}
|
||||||
sort.Sort(types.TxByNonce(m.cache))
|
sort.Sort(types.TxByNonce(m.cache))
|
||||||
}
|
}
|
||||||
|
return m.cache
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||||
|
// sorted internal representation. The result of the sorting is cached in case
|
||||||
|
// it's requested again before any modifications are made to the contents.
|
||||||
|
func (m *txSortedMap) Flatten() types.Transactions {
|
||||||
// Copy the cache to prevent accidental modifications
|
// Copy the cache to prevent accidental modifications
|
||||||
txs := make(types.Transactions, len(m.cache))
|
cache := m.flatten()
|
||||||
copy(txs, m.cache)
|
txs := make(types.Transactions, len(cache))
|
||||||
|
copy(txs, cache)
|
||||||
return txs
|
return txs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LastElement returns the last element of a flattened list, thus, the
|
||||||
|
// transaction with the highest nonce
|
||||||
|
func (m *txSortedMap) LastElement() *types.Transaction {
|
||||||
|
cache := m.flatten()
|
||||||
|
return cache[len(cache)-1]
|
||||||
|
}
|
||||||
|
|
||||||
// txList is a "list" of transactions belonging to an account, sorted by account
|
// txList is a "list" of transactions belonging to an account, sorted by account
|
||||||
// nonce. The same type can be used both for storing contiguous transactions for
|
// nonce. The same type can be used both for storing contiguous transactions for
|
||||||
// the executable/pending queue; and for storing gapped transactions for the non-
|
// the executable/pending queue; and for storing gapped transactions for the non-
|
||||||
|
@ -223,7 +251,7 @@ type txList struct {
|
||||||
strict bool // Whether nonces are strictly continuous or not
|
strict bool // Whether nonces are strictly continuous or not
|
||||||
txs *txSortedMap // Heap indexed sorted hash map of the transactions
|
txs *txSortedMap // Heap indexed sorted hash map of the transactions
|
||||||
|
|
||||||
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
|
costcap uint64 // Price of the highest costing transaction (reset only if exceeds balance)
|
||||||
gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
|
gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +261,6 @@ func newTxList(strict bool) *txList {
|
||||||
return &txList{
|
return &txList{
|
||||||
strict: strict,
|
strict: strict,
|
||||||
txs: newTxSortedMap(),
|
txs: newTxSortedMap(),
|
||||||
costcap: new(big.Int),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +279,11 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
|
||||||
// If there's an older better transaction, abort
|
// If there's an older better transaction, abort
|
||||||
old := l.txs.Get(tx.Nonce())
|
old := l.txs.Get(tx.Nonce())
|
||||||
if old != nil {
|
if old != nil {
|
||||||
threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100))
|
// threshold = oldGP * (100 + priceBump) / 100
|
||||||
|
a := big.NewInt(100 + int64(priceBump))
|
||||||
|
a = a.Mul(a, old.GasPrice())
|
||||||
|
b := big.NewInt(100)
|
||||||
|
threshold := a.Div(a, b)
|
||||||
// Have to ensure that the new gas price is higher than the old gas
|
// Have to ensure that the new gas price is higher than the old gas
|
||||||
// price as well as checking the percentage threshold to ensure that
|
// price as well as checking the percentage threshold to ensure that
|
||||||
// this is accurate for low (Wei-level) gas price replacements
|
// this is accurate for low (Wei-level) gas price replacements
|
||||||
|
@ -260,9 +291,14 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cost, overflow := tx.CostU64()
|
||||||
|
if overflow {
|
||||||
|
log.Warn("transaction cost overflown, txHash: %v txCost: %v", tx.Hash(), cost)
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
// Otherwise overwrite the old transaction with the current one
|
// Otherwise overwrite the old transaction with the current one
|
||||||
l.txs.Put(tx)
|
l.txs.Put(tx)
|
||||||
if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
|
if l.costcap < cost {
|
||||||
l.costcap = cost
|
l.costcap = cost
|
||||||
}
|
}
|
||||||
if gas := tx.Gas(); l.gascap < gas {
|
if gas := tx.Gas(); l.gascap < gas {
|
||||||
|
@ -287,29 +323,35 @@ func (l *txList) Forward(threshold uint64) types.Transactions {
|
||||||
// a point in calculating all the costs or if the balance covers all. If the threshold
|
// a point in calculating all the costs or if the balance covers all. If the threshold
|
||||||
// is lower than the costgas cap, the caps will be reset to a new high after removing
|
// is lower than the costgas cap, the caps will be reset to a new high after removing
|
||||||
// the newly invalidated transactions.
|
// the newly invalidated transactions.
|
||||||
func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) {
|
func (l *txList) Filter(costLimit uint64, gasLimit uint64) (types.Transactions, types.Transactions) {
|
||||||
// If all transactions are below the threshold, short circuit
|
// If all transactions are below the threshold, short circuit
|
||||||
if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit {
|
if l.costcap <= costLimit && l.gascap <= gasLimit {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds
|
l.costcap = costLimit // Lower the caps to the thresholds
|
||||||
l.gascap = gasLimit
|
l.gascap = gasLimit
|
||||||
|
|
||||||
// Filter out all the transactions above the account's funds
|
// Filter out all the transactions above the account's funds
|
||||||
removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(costLimit) > 0 || tx.Gas() > gasLimit })
|
removed := l.txs.filter(func(tx *types.Transaction) bool {
|
||||||
|
cost, _ := tx.CostU64()
|
||||||
|
return cost > costLimit || tx.Gas() > gasLimit
|
||||||
|
})
|
||||||
|
|
||||||
// If the list was strict, filter anything above the lowest nonce
|
if len(removed) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
var invalids types.Transactions
|
var invalids types.Transactions
|
||||||
|
// If the list was strict, filter anything above the lowest nonce
|
||||||
if l.strict && len(removed) > 0 {
|
if l.strict {
|
||||||
lowest := uint64(math.MaxUint64)
|
lowest := uint64(math.MaxUint64)
|
||||||
for _, tx := range removed {
|
for _, tx := range removed {
|
||||||
if nonce := tx.Nonce(); lowest > nonce {
|
if nonce := tx.Nonce(); lowest > nonce {
|
||||||
lowest = nonce
|
lowest = nonce
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
|
invalids = l.txs.filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
|
||||||
}
|
}
|
||||||
|
l.txs.reheap()
|
||||||
return removed, invalids
|
return removed, invalids
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,6 +405,12 @@ func (l *txList) Flatten() types.Transactions {
|
||||||
return l.txs.Flatten()
|
return l.txs.Flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LastElement returns the last element of a flattened list, thus, the
|
||||||
|
// transaction with the highest nonce
|
||||||
|
func (l *txList) LastElement() *types.Transaction {
|
||||||
|
return l.txs.LastElement()
|
||||||
|
}
|
||||||
|
|
||||||
// priceHeap is a heap.Interface implementation over transactions for retrieving
|
// priceHeap is a heap.Interface implementation over transactions for retrieving
|
||||||
// price-sorted transactions to discard when the pool fills up.
|
// price-sorted transactions to discard when the pool fills up.
|
||||||
type priceHeap []*types.Transaction
|
type priceHeap []*types.Transaction
|
||||||
|
@ -495,8 +543,29 @@ func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) boo
|
||||||
// Discard finds a number of most underpriced transactions, removes them from the
|
// Discard finds a number of most underpriced transactions, removes them from the
|
||||||
// priced list and returns them for further removal from the entire pool.
|
// priced list and returns them for further removal from the entire pool.
|
||||||
func (l *txPricedList) Discard(slots int, local *accountSet) types.Transactions {
|
func (l *txPricedList) Discard(slots int, local *accountSet) types.Transactions {
|
||||||
|
// If we have some local accountset, those will not be discarded
|
||||||
|
if !local.empty() {
|
||||||
|
// In case the list is filled to the brim with 'local' txs, we do this
|
||||||
|
// little check to avoid unpacking / repacking the heap later on, which
|
||||||
|
// is very expensive
|
||||||
|
discardable := 0
|
||||||
|
for _, tx := range *l.items {
|
||||||
|
if !local.containsTx(tx) {
|
||||||
|
discardable++
|
||||||
|
}
|
||||||
|
if discardable >= slots {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if slots > discardable {
|
||||||
|
slots = discardable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if slots == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop
|
drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop
|
||||||
save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
|
save := make(types.Transactions, 0, len(*l.items)-slots) // Local underpriced transactions to keep
|
||||||
|
|
||||||
for len(*l.items) > 0 && slots > 0 {
|
for len(*l.items) > 0 && slots > 0 {
|
||||||
// Discard stale transactions if found during cleanup
|
// Discard stale transactions if found during cleanup
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -51,20 +50,22 @@ func TestStrictTxListAdd(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkTxListAdd(t *testing.B) {
|
func BenchmarkTxListAdd(b *testing.B) {
|
||||||
// Generate a list of transactions to insert
|
// Generate a list of transactions to insert
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
|
|
||||||
txs := make(types.Transactions, 100000)
|
txs := make(types.Transactions, 2000)
|
||||||
for i := 0; i < len(txs); i++ {
|
for i := 0; i < len(txs); i++ {
|
||||||
txs[i] = transaction(uint64(i), 0, key)
|
txs[i] = transaction(uint64(i), 0, key)
|
||||||
}
|
}
|
||||||
// Insert the transactions in a random order
|
// Insert the transactions in a random order
|
||||||
|
b.ResetTimer()
|
||||||
|
priceLimit := DefaultTxPoolConfig.PriceLimit
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
list := newTxList(true)
|
list := newTxList(true)
|
||||||
priceLimit := big.NewInt(int64(DefaultTxPoolConfig.PriceLimit))
|
|
||||||
t.ResetTimer()
|
|
||||||
for _, v := range rand.Perm(len(txs)) {
|
for _, v := range rand.Perm(len(txs)) {
|
||||||
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
|
list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
|
||||||
list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump)
|
list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -543,7 +543,11 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||||
}
|
}
|
||||||
// Transactor should have enough funds to cover the costs
|
// Transactor should have enough funds to cover the costs
|
||||||
// cost == V + GP * GL
|
// cost == V + GP * GL
|
||||||
if pool.currentState.GetBalance(from).Cmp(tx.Cost()) < 0 {
|
cost, overflow := tx.CostU64()
|
||||||
|
if overflow {
|
||||||
|
return ErrInsufficientFunds
|
||||||
|
}
|
||||||
|
if pool.currentState.GetBalance(from).Uint64() < cost {
|
||||||
return ErrInsufficientFunds
|
return ErrInsufficientFunds
|
||||||
}
|
}
|
||||||
// Ensure the transaction has more gas than the basic tx fee.
|
// Ensure the transaction has more gas than the basic tx fee.
|
||||||
|
@ -1059,8 +1063,8 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
|
||||||
|
|
||||||
// Update all accounts to the latest known pending nonce
|
// Update all accounts to the latest known pending nonce
|
||||||
for addr, list := range pool.pending {
|
for addr, list := range pool.pending {
|
||||||
txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway
|
highestPending := list.LastElement()
|
||||||
pool.pendingNonces.set(addr, txs[len(txs)-1].Nonce()+1)
|
pool.pendingNonces.set(addr, highestPending.Nonce()+1)
|
||||||
}
|
}
|
||||||
pool.mu.Unlock()
|
pool.mu.Unlock()
|
||||||
|
|
||||||
|
@ -1190,7 +1194,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
|
||||||
}
|
}
|
||||||
log.Trace("Removed old queued transactions", "count", len(forwards))
|
log.Trace("Removed old queued transactions", "count", len(forwards))
|
||||||
// Drop all transactions that are too costly (low balance or out of gas)
|
// Drop all transactions that are too costly (low balance or out of gas)
|
||||||
drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas)
|
drops, _ := list.Filter(pool.currentState.GetBalance(addr).Uint64(), pool.currentMaxGas)
|
||||||
for _, tx := range drops {
|
for _, tx := range drops {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
pool.all.Remove(hash)
|
pool.all.Remove(hash)
|
||||||
|
@ -1382,7 +1386,7 @@ func (pool *TxPool) demoteUnexecutables() {
|
||||||
log.Trace("Removed old pending transaction", "hash", hash)
|
log.Trace("Removed old pending transaction", "hash", hash)
|
||||||
}
|
}
|
||||||
// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
|
// Drop all transactions that are too costly (low balance or out of gas), and queue any invalids back for later
|
||||||
drops, invalids := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas)
|
drops, invalids := list.Filter(pool.currentState.GetBalance(addr).Uint64(), pool.currentMaxGas)
|
||||||
for _, tx := range drops {
|
for _, tx := range drops {
|
||||||
hash := tx.Hash()
|
hash := tx.Hash()
|
||||||
log.Trace("Removed unpayable pending transaction", "hash", hash)
|
log.Trace("Removed unpayable pending transaction", "hash", hash)
|
||||||
|
@ -1457,6 +1461,10 @@ func (as *accountSet) contains(addr common.Address) bool {
|
||||||
return exist
|
return exist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (as *accountSet) empty() bool {
|
||||||
|
return len(as.accounts) == 0
|
||||||
|
}
|
||||||
|
|
||||||
// containsTx checks if the sender of a given tx is within the set. If the sender
|
// containsTx checks if the sender of a given tx is within the set. If the sender
|
||||||
// cannot be derived, this method returns false.
|
// cannot be derived, this method returns false.
|
||||||
func (as *accountSet) containsTx(tx *types.Transaction) bool {
|
func (as *accountSet) containsTx(tx *types.Transaction) bool {
|
||||||
|
|
|
@ -1889,11 +1889,15 @@ func benchmarkFuturePromotion(b *testing.B, size int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Benchmarks the speed of batched transaction insertion.
|
// Benchmarks the speed of batched transaction insertion.
|
||||||
func BenchmarkPoolBatchInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100) }
|
func BenchmarkPoolBatchInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100, false) }
|
||||||
func BenchmarkPoolBatchInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000) }
|
func BenchmarkPoolBatchInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000, false) }
|
||||||
func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000) }
|
func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000, false) }
|
||||||
|
|
||||||
func benchmarkPoolBatchInsert(b *testing.B, size int) {
|
func BenchmarkPoolBatchLocalInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100, true) }
|
||||||
|
func BenchmarkPoolBatchLocalInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000, true) }
|
||||||
|
func BenchmarkPoolBatchLocalInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000, true) }
|
||||||
|
|
||||||
|
func benchmarkPoolBatchInsert(b *testing.B, size int, local bool) {
|
||||||
// Generate a batch of transactions to enqueue into the pool
|
// Generate a batch of transactions to enqueue into the pool
|
||||||
pool, key := setupTxPool()
|
pool, key := setupTxPool()
|
||||||
defer pool.Stop()
|
defer pool.Stop()
|
||||||
|
@ -1911,6 +1915,10 @@ func benchmarkPoolBatchInsert(b *testing.B, size int) {
|
||||||
// Benchmark importing the transactions into the queue
|
// Benchmark importing the transactions into the queue
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for _, batch := range batches {
|
for _, batch := range batches {
|
||||||
|
if local {
|
||||||
|
pool.AddLocals(batch)
|
||||||
|
} else {
|
||||||
pool.AddRemotes(batch)
|
pool.AddRemotes(batch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
@ -41,6 +42,7 @@ type Transaction struct {
|
||||||
hash atomic.Value
|
hash atomic.Value
|
||||||
size atomic.Value
|
size atomic.Value
|
||||||
from atomic.Value
|
from atomic.Value
|
||||||
|
cost atomic.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
type txdata struct {
|
type txdata struct {
|
||||||
|
@ -258,6 +260,15 @@ func (tx *Transaction) Cost() *big.Int {
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *Transaction) CostU64() (uint64, bool) {
|
||||||
|
if tx.data.Price.BitLen() > 63 || tx.data.Amount.BitLen() > 63 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
cost, overflowMul := math.SafeMul(tx.data.Price.Uint64(), tx.data.GasLimit)
|
||||||
|
total, overflowAdd := math.SafeAdd(cost, tx.data.Amount.Uint64())
|
||||||
|
return total, overflowMul || overflowAdd
|
||||||
|
}
|
||||||
|
|
||||||
// RawSignatureValues returns the V, R, S signature values of the transaction.
|
// RawSignatureValues returns the V, R, S signature values of the transaction.
|
||||||
// The return values should not be modified by the caller.
|
// The return values should not be modified by the caller.
|
||||||
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
|
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
|
||||||
|
|
Loading…
Reference in New Issue