more changes (wip)

This commit is contained in:
Jared Wasinger 2026-05-06 12:00:33 -04:00
parent b01202e3fe
commit 90175152bb
7 changed files with 51 additions and 165 deletions

View file

@ -655,13 +655,6 @@ func (bc *BlockChain) processBlockWithAccessList(parentRoot common.Hash, block *
// AccountLoaded/StorageLoaded come from the BAL access list (deduplicated);
// per-StateDB sums would over-count addresses touched by multiple phases.
stats.StateCounts = res.Counts
stats.StateCounts.Add(stateTransition.WriteCounts())
if al := block.AccessList(); al != nil {
stats.StateCounts.AccountLoaded = al.UniqueAccountCount()
stats.StateCounts.StorageLoaded = al.UniqueStorageSlotCount()
}
stats.StateCounts.CodeLoaded = res.CodeLoaded
stats.StateCounts.CodeLoadBytes = res.CodeLoadBytes
stats.Execution = res.ExecTime
stats.ExecWall = res.ExecTime
@ -674,19 +667,13 @@ func (bc *BlockChain) processBlockWithAccessList(parentRoot common.Hash, block *
stats.DatabaseCommit = m.TrieDBCommits
stats.Prefetch = m.StatePrefetch
}
// Sum-of-CPU-time across per-tx, BAL state-transition, and prefetcher paths.
var prefetchAccountReads, prefetchStorageReads time.Duration
if pr, ok := prefetchReader.(interface {
PrefetchReadTimes() (time.Duration, time.Duration)
}); ok {
prefetchAccountReads, prefetchStorageReads = pr.PrefetchReadTimes()
}
prefetchAccountReads, prefetchStorageReads = prefetchReader.(*.PrefetchReadTimes()
balAccountReads, balStorageReads := stateTransition.ReadTimes()
stats.AccountReads = res.Reads.Account + prefetchAccountReads + balAccountReads
stats.StorageReads = res.Reads.Storage + prefetchStorageReads + balStorageReads
stats.CodeReads = res.Reads.Code
stats.Prefetch = prefetchReader.(state.PrefetcherMetricer).Metrics().Elapsed
/*
stats.AccountReads = res.Reads.Account + prefetchAccountReads + balAccountReads
stats.StorageReads = res.Reads.Storage + prefetchStorageReads + balStorageReads
stats.CodeReads = res.Reads.Code
*/
if r, ok := prefetchReader.(state.ReaderStater); ok {
stats.StateReadCacheStats = r.GetStats()

View file

@ -38,9 +38,7 @@ type ExecuteStats struct {
StorageCommits time.Duration // Time spent on the storage trie commit
CodeReads time.Duration // Time spent on the contract code read
// State-mutation counts. StorageUpdated/StorageDeleted are int64
// (snapshot from atomic.Int64 on StateDB).
state.StateCounts
// TODO: where is code bytes loaded metric?
Execution time.Duration // Time spent on the EVM execution
Validation time.Duration // Time spent on the block validation

View file

@ -3,7 +3,6 @@ package state
import (
"maps"
"sync"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
@ -41,17 +40,6 @@ type BALStateTransition struct {
tries sync.Map //map[common.Address]Trie
deletions map[common.Address]struct{}
// Storage/read counters are atomic — written from per-address goroutines.
// account/code counters are plain int — written single-threaded.
accountDeleted int
accountUpdated int
storageDeleted atomic.Int64
storageUpdated atomic.Int64
codeUpdated int
codeUpdateBytes int
accountReadNS atomic.Int64
storageReadNS atomic.Int64
stateUpdate *stateUpdate
metrics BALStateTransitionMetrics
@ -64,23 +52,6 @@ func (s *BALStateTransition) Metrics() *BALStateTransitionMetrics {
return &s.metrics
}
// ReadTimes returns the accumulated state-read times.
func (s *BALStateTransition) ReadTimes() (account, storage time.Duration) {
return time.Duration(s.accountReadNS.Load()), time.Duration(s.storageReadNS.Load())
}
// WriteCounts returns the state-mutation counts from the parallel state-root pass.
func (s *BALStateTransition) WriteCounts() StateCounts {
return StateCounts{
AccountUpdated: s.accountUpdated,
AccountDeleted: s.accountDeleted,
StorageUpdated: s.storageUpdated.Load(),
StorageDeleted: s.storageDeleted.Load(),
CodeUpdated: s.codeUpdated,
CodeUpdateBytes: s.codeUpdateBytes,
}
}
type BALStateTransitionMetrics struct {
// trie hashing metrics
AccountUpdate time.Duration
@ -223,9 +194,7 @@ func (s *BALStateTransition) commitAccount(addr common.Address) (*accountUpdate,
for key, value := range s.diffs[addr].StorageWrites {
hash := crypto.Keccak256Hash(key[:])
op.storages[hash] = encode(value)
storageReadStart := time.Now()
storage, err := s.reader.Storage(addr, key)
s.storageReadNS.Add(time.Since(storageReadStart).Nanoseconds())
if err != nil {
return nil, nil, err
}
@ -365,10 +334,13 @@ func (s *BALStateTransition) CommitWithUpdate(block uint64, deleteEmptyObjects b
return common.Hash{}, nil, err
}
accountUpdatedMeter.Mark(int64(s.accountUpdated))
storageUpdatedMeter.Mark(s.storageUpdated.Load())
accountDeletedMeter.Mark(int64(s.accountDeleted))
storageDeletedMeter.Mark(s.storageDeleted.Load())
/*
TODO: derive these from the BAL
accountUpdatedMeter.Mark(int64(s.accountUpdated))
storageUpdatedMeter.Mark(s.storageUpdated.Load())
accountDeletedMeter.Mark(int64(s.accountDeleted))
storageDeletedMeter.Mark(s.storageDeleted.Load())
*/
accountTrieUpdatedMeter.Mark(int64(accountTrieNodesUpdated))
accountTrieDeletedMeter.Mark(int64(accountTrieNodesDeleted))
storageTriesUpdatedMeter.Mark(int64(storageTrieNodesUpdated))
@ -421,9 +393,7 @@ func (s *BALStateTransition) IntermediateRoot(_ bool) common.Hash {
defer wg.Done()
// 1 (c): update each mutated account, producing the post-block state object by applying the state mutations to the prestate (retrieved in 1a).
accountReadStart := time.Now()
acct, err := s.reader.Account(address)
s.accountReadNS.Add(time.Since(accountReadStart).Nanoseconds())
if err != nil {
s.setError(err)
return
@ -450,12 +420,8 @@ func (s *BALStateTransition) IntermediateRoot(_ bool) common.Hash {
if val != (common.Hash{}) {
updateKeys = append(updateKeys, key[:])
updateValues = append(updateValues, common.TrimLeftZeroes(val[:]))
s.storageUpdated.Add(1)
} else {
deleteKeys = append(deleteKeys, key[:])
s.storageDeleted.Add(1)
}
}
if err := tr.UpdateStorageBatch(address, updateKeys, updateValues); err != nil {
@ -509,7 +475,6 @@ func (s *BALStateTransition) IntermediateRoot(_ bool) common.Hash {
return common.Hash{}
}
s.deletions[mutatedAddr] = struct{}{}
s.accountDeleted++
} else {
acct, code := s.updateAccount(mutatedAddr)
@ -520,15 +485,12 @@ func (s *BALStateTransition) IntermediateRoot(_ bool) common.Hash {
s.setError(err)
return common.Hash{}
}
s.codeUpdated++
s.codeUpdateBytes += len(code)
}
if err := s.stateTrie.UpdateAccount(mutatedAddr, acct, len(code)); err != nil {
s.setError(err)
return common.Hash{}
}
s.postStates[mutatedAddr] = acct
s.accountUpdated++
}
}

View file

@ -18,8 +18,6 @@ package state
import (
"fmt"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/overlay"
"github.com/ethereum/go-ethereum/core/rawdb"
@ -241,26 +239,6 @@ func (db *CachingDB) ReadersWithCacheStats(stateRoot common.Hash) (Reader, Reade
return ra, rb, nil
}
type ReaderEIP7928Metrics struct {
// the total amount of time it took to complete the scheduled workload
WallElapsed time.Duration
// the aggregated total time spent on state loading by all workers
TotalElapsed time.Duration
// the amount of accounts loaded
Accounts int
// the amount of storage slots loaded
Storages int
// number of accounts with code loaded
Codes int
// total amount of code bytes loaded
CodeBytes int
}
type ReaderEIP7928 interface {
Reader
Metrics() *ReaderEIP7928Metrics
}
// ReaderEIP7928 creates a state reader with the manner of Block-level accessList.
func (db *CachingDB) ReaderEIP7928(stateRoot common.Hash, accessList map[common.Address][]common.Hash, threads int) (Reader, error) {
base, err := db.StateReader(stateRoot)

View file

@ -18,10 +18,6 @@ package state
import (
"errors"
"sync"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/overlay"
"github.com/ethereum/go-ethereum/core/types"
@ -32,6 +28,8 @@ import (
"github.com/ethereum/go-ethereum/trie/transitiontrie"
"github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/database"
"sync"
"sync/atomic"
)
// ContractCodeReader defines the interface for accessing contract code.
@ -564,13 +562,3 @@ func (r *reader) GetStats() ReaderStats {
StateStats: r.GetStateStats(),
}
}
// PrefetchReadTimes forwards to the wrapped prefetcher, or returns zero.
func (r *reader) PrefetchReadTimes() (account, storage time.Duration) {
if pr, ok := r.StateReader.(interface {
PrefetchReadTimes() (time.Duration, time.Duration)
}); ok {
return pr.PrefetchReadTimes()
}
return 0, 0
}

View file

@ -64,7 +64,6 @@ package state
import (
"sync"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/crypto"
@ -88,10 +87,33 @@ type prefetchStateReader struct {
done chan struct{}
term chan struct{}
closeOnce sync.Once
start time.Time
metrics PrefetchMetrics
}
// Atomic — process() runs across N goroutines.
accountReadNS atomic.Int64
storageReadNS atomic.Int64
type PrefetchMetrics struct {
// the total amount of time it took to complete the scheduled workload
Elapsed time.Duration
// the aggregated total time spent on state loading by all workers
// TODO (jwasinger): add back in after i finish initial commit(s) with only the changes I think will be ultimately merged
// TotalElapsed time.Duration
/*
// TODO: source these from the other reader where they are implemented
// the amount of accounts loaded
Accounts int
// the amount of storage slots loaded
Storages int
// number of accounts with code loaded
Codes int
// total amount of code bytes loaded
CodeBytes int
}
*/
}
type PrefetcherMetricer interface {
Metrics() PrefetchMetrics
}
func newPrefetchStateReader(reader StateReader, accessList bal.StorageKeys, nThreads int) *prefetchStateReader {
@ -186,13 +208,9 @@ func (r *prefetchStateReader) process(start, limit int) {
return
default:
if j == 0 {
accountReadStart := time.Now()
r.StateReader.Account(t.addr)
r.accountReadNS.Add(time.Since(accountReadStart).Nanoseconds())
} else {
storageReadStart := time.Now()
r.StateReader.Storage(t.addr, t.slots[j-1])
r.storageReadNS.Add(time.Since(storageReadStart).Nanoseconds())
}
}
}
@ -373,6 +391,9 @@ func (r *readerTracker) TouchStorage(addr common.Address, slot common.Hash) {
list[slot] = struct{}{}
}
/*
// TODO: ensure these are accounted for
// GetStateStats forwards stats from the wrapped reader; without this, BAL
// blocks would emit zero cache hit/miss counts.
func (r *prefetchStateReader) GetStateStats() StateReaderStats {
@ -387,3 +408,4 @@ func (r *prefetchStateReader) GetStateStats() StateReaderStats {
func (r *prefetchStateReader) PrefetchReadTimes() (account, storage time.Duration) {
return time.Duration(r.accountReadNS.Load()), time.Duration(r.storageReadNS.Load())
}
*/

View file

@ -223,6 +223,10 @@ func (s *StateDB) WithReader(reader Reader) *StateDB {
return cpy
}
/*
// TODO (jwasinger): add these back in a subsequent commit
// ReadDurations groups the {Account, Storage, Code} state-read times.
// Sum-of-CPU-time when aggregated across BAL phase statedbs.
type ReadDurations struct {
@ -237,60 +241,7 @@ func (r *ReadDurations) Add(other ReadDurations) {
r.Storage += other.Storage
r.Code += other.Code
}
// StateCounts is a plain-int snapshot of state-mutation counters. Atomic
// fields on StateDB are Load()'d at the SnapshotCounts boundary.
type StateCounts struct {
AccountLoaded int // accounts retrieved from the database during the state transition
AccountUpdated int // accounts updated during the state transition
AccountDeleted int // accounts deleted during the state transition
StorageLoaded int // storage slots retrieved from the database during the state transition
StorageUpdated int64 // storage slots updated (snapshotted from atomic on StateDB)
StorageDeleted int64 // storage slots deleted (snapshotted from atomic on StateDB)
CodeLoaded int // contract code reads
CodeLoadBytes int // total bytes of resolved code
CodeUpdated int // code writes (CREATE/CREATE2/EIP-7702)
CodeUpdateBytes int // total bytes of persisted code written
}
// Add merges other into c.
func (c *StateCounts) Add(other StateCounts) {
c.AccountLoaded += other.AccountLoaded
c.AccountUpdated += other.AccountUpdated
c.AccountDeleted += other.AccountDeleted
c.StorageLoaded += other.StorageLoaded
c.StorageUpdated += other.StorageUpdated
c.StorageDeleted += other.StorageDeleted
c.CodeLoaded += other.CodeLoaded
c.CodeLoadBytes += other.CodeLoadBytes
c.CodeUpdated += other.CodeUpdated
c.CodeUpdateBytes += other.CodeUpdateBytes
}
// SnapshotCounts returns a plain-int copy of the state-mutation counters.
func (s *StateDB) SnapshotCounts() StateCounts {
return StateCounts{
AccountLoaded: s.AccountLoaded,
AccountUpdated: s.AccountUpdated,
AccountDeleted: s.AccountDeleted,
StorageLoaded: s.StorageLoaded,
StorageUpdated: s.StorageUpdated.Load(),
StorageDeleted: s.StorageDeleted.Load(),
CodeLoaded: s.CodeLoaded,
CodeLoadBytes: s.CodeLoadBytes,
CodeUpdated: s.CodeUpdated,
CodeUpdateBytes: s.CodeUpdateBytes,
}
}
// SnapshotReads returns the {Account, Storage, Code} read durations.
func (s *StateDB) SnapshotReads() ReadDurations {
return ReadDurations{
Account: s.AccountReads,
Storage: s.StorageReads,
Code: s.CodeReads,
}
}
*/
// SnapshotCodeLoads returns addresses whose code body was fetched, mapped to
// byte length. Used to deduplicate code-load events across BAL phase StateDBs.