core: drop and tighten comments per PR feedback

This commit is contained in:
CPerezz 2026-05-04 18:42:03 +02:00
parent 3cdb836553
commit 51fdf0e053
No known key found for this signature in database
GPG key ID: 62045F34B97177DD
8 changed files with 62 additions and 146 deletions

View file

@ -652,10 +652,8 @@ func (bc *BlockChain) processBlockWithAccessList(parentRoot common.Hash, block *
writeTime := time.Since(writeStart) writeTime := time.Since(writeStart)
var stats ExecuteStats var stats ExecuteStats
// Counts: write counts come from the BAL state transition; read counts // AccountLoaded/StorageLoaded come from the BAL access list (deduplicated);
// for accounts/storage come from the BAL access list (deduplicated); // per-StateDB sums would over-count addresses touched by multiple phases.
// code-load counts come from a deduplicated address set tracked across
// all phase StateDBs by the parallel processor.
stats.StateCounts = res.Counts stats.StateCounts = res.Counts
stats.StateCounts.Add(stateTransition.WriteCounts()) stats.StateCounts.Add(stateTransition.WriteCounts())
if al := block.AccessList(); al != nil { if al := block.AccessList(); al != nil {
@ -665,17 +663,10 @@ func (bc *BlockChain) processBlockWithAccessList(parentRoot common.Hash, block *
stats.StateCounts.CodeLoaded = res.CodeLoaded stats.StateCounts.CodeLoaded = res.CodeLoaded
stats.StateCounts.CodeLoadBytes = res.CodeLoadBytes stats.StateCounts.CodeLoadBytes = res.CodeLoadBytes
// Time durations under parallel execution use wall-clock semantics. stats.Execution = res.ExecTime
// Per-tx duration sums (CPU-time) are intentionally not plumbed: they
// would conflict with mgas/sec accounting against TotalTime.
stats.Execution = res.ExecTime // wall-clock parallel execution
stats.ExecWall = res.ExecTime stats.ExecWall = res.ExecTime
stats.PostProcess = res.PostProcessTime stats.PostProcess = res.PostProcessTime
// Map BALStateTransitionMetrics (already wall-clock-correct) onto schema
// fields used by logSlow's StateHashMs computation. The sum
// AccountUpdate+StateUpdate+StateHash is the parallel state-root compute
// time, matching reportBALMetrics's stateRootComputeTimer.
if m := res.StateTransitionMetrics; m != nil { if m := res.StateTransitionMetrics; m != nil {
stats.AccountHashes = m.AccountUpdate + m.StateUpdate + m.StateHash stats.AccountHashes = m.AccountUpdate + m.StateUpdate + m.StateHash
stats.AccountCommits = m.AccountCommits stats.AccountCommits = m.AccountCommits
@ -683,10 +674,7 @@ func (bc *BlockChain) processBlockWithAccessList(parentRoot common.Hash, block *
stats.DatabaseCommit = m.TrieDBCommits stats.DatabaseCommit = m.TrieDBCommits
stats.Prefetch = m.StatePrefetch stats.Prefetch = m.StatePrefetch
} }
// Sum read times across per-tx execution, BAL state-transition, and // Sum-of-CPU-time across per-tx, BAL state-transition, and prefetcher paths.
// prefetcher async fetches. Sum-of-CPU-time, not wall-clock. No
// WaitPrefetch needed: state is already committed, so the prefetcher
// (bounded by BAL contents) has drained.
var prefetchAccountReads, prefetchStorageReads time.Duration var prefetchAccountReads, prefetchStorageReads time.Duration
if pr, ok := prefetchReader.(interface { if pr, ok := prefetchReader.(interface {
PrefetchReadTimes() (time.Duration, time.Duration) PrefetchReadTimes() (time.Duration, time.Duration)
@ -698,7 +686,6 @@ func (bc *BlockChain) processBlockWithAccessList(parentRoot common.Hash, block *
stats.StorageReads = res.Reads.Storage + prefetchStorageReads + balStorageReads stats.StorageReads = res.Reads.Storage + prefetchStorageReads + balStorageReads
stats.CodeReads = res.Reads.Code stats.CodeReads = res.Reads.Code
// Cache stats from the shared prefetch reader (accumulates centrally).
if r, ok := prefetchReader.(state.ReaderStater); ok { if r, ok := prefetchReader.(state.ReaderStater); ok {
stats.StateReadCacheStats = r.GetStats() stats.StateReadCacheStats = r.GetStats()
} }

View file

@ -28,9 +28,7 @@ import (
// ExecuteStats includes all the statistics of a block execution in details. // ExecuteStats includes all the statistics of a block execution in details.
type ExecuteStats struct { type ExecuteStats struct {
// State read times. For BAL blocks these are sum-of-CPU-time across // State read times
// per-tx, pre-tx, post-tx, BAL state-transition and prefetcher paths;
// can exceed TotalTime by design. Sequential blocks: wall-clock.
AccountReads time.Duration // Time spent on the account reads AccountReads time.Duration // Time spent on the account reads
StorageReads time.Duration // Time spent on the storage reads StorageReads time.Duration // Time spent on the storage reads
AccountHashes time.Duration // Time spent on the account trie hash AccountHashes time.Duration // Time spent on the account trie hash
@ -40,8 +38,7 @@ type ExecuteStats struct {
StorageCommits time.Duration // Time spent on the storage trie commit StorageCommits time.Duration // Time spent on the storage trie commit
CodeReads time.Duration // Time spent on the contract code read CodeReads time.Duration // Time spent on the contract code read
// Embedded state-mutation counts. Field promotion preserves access as // State-mutation counts. StorageUpdated/StorageDeleted are int64
// s.AccountLoaded etc. Note StorageUpdated/StorageDeleted are int64 here
// (snapshot from atomic.Int64 on StateDB). // (snapshot from atomic.Int64 on StateDB).
state.StateCounts state.StateCounts
@ -55,9 +52,7 @@ type ExecuteStats struct {
TotalTime time.Duration // The total time spent on block execution TotalTime time.Duration // The total time spent on block execution
MgasPerSecond float64 // The million gas processed per second MgasPerSecond float64 // The million gas processed per second
// BAL extension durations — set by processBlockWithAccessList for blocks // BAL parallel-path durations, surfaced under slowBlockLog.BAL.
// processed via the parallel BAL path. Surfaced in the slow-block log's
// optional `bal` block.
ExecWall time.Duration // Wall-clock parallel transaction execution ExecWall time.Duration // Wall-clock parallel transaction execution
PostProcess time.Duration // Post-tx finalization (system contracts, requests) PostProcess time.Duration // Post-tx finalization (system contracts, requests)
Prefetch time.Duration // BAL state prefetching Prefetch time.Duration // BAL state prefetching
@ -123,9 +118,7 @@ type slowBlockLog struct {
StateReads slowBlockReads `json:"state_reads"` StateReads slowBlockReads `json:"state_reads"`
StateWrites slowBlockWrites `json:"state_writes"` StateWrites slowBlockWrites `json:"state_writes"`
Cache slowBlockCache `json:"cache"` Cache slowBlockCache `json:"cache"`
// BAL is the parallel-execution extension. Present iff the block was // BAL is set only for blocks processed via the parallel BAL path.
// processed via the BAL parallel path. Cross-client consumers can use its
// presence to distinguish parallel-executed blocks from sequential ones.
BAL *slowBlockBAL `json:"bal,omitempty"` BAL *slowBlockBAL `json:"bal,omitempty"`
} }
@ -187,31 +180,18 @@ type slowBlockCodeCacheEntry struct {
MissBytes int64 `json:"miss_bytes"` MissBytes int64 `json:"miss_bytes"`
} }
// slowBlockBAL is the parallel-execution extension surfaced under the // slowBlockBAL holds parallel-execution timings that don't fit the sequential schema.
// optional "bal" field of slowBlockLog. It carries timings that are
// well-defined under parallel execution but don't fit the sequential schema.
type slowBlockBAL struct { type slowBlockBAL struct {
// ExecWallMs is wall-clock parallel transaction execution.
ExecWallMs float64 `json:"exec_wall_ms"` ExecWallMs float64 `json:"exec_wall_ms"`
// PostProcessMs is post-tx system contracts (withdrawals, consolidations, finalize).
PostProcessMs float64 `json:"post_process_ms"` PostProcessMs float64 `json:"post_process_ms"`
// PrefetchMs is the BAL state prefetcher (alias of state_prefetch_ms).
PrefetchMs float64 `json:"prefetch_ms"` PrefetchMs float64 `json:"prefetch_ms"`
// StatePrefetchMs is async state-load time during state-root computation.
StatePrefetchMs float64 `json:"state_prefetch_ms"` StatePrefetchMs float64 `json:"state_prefetch_ms"`
// AccountUpdateMs is the account trie update phase.
AccountUpdateMs float64 `json:"account_update_ms"` AccountUpdateMs float64 `json:"account_update_ms"`
// StateUpdateMs is the state trie update phase.
StateUpdateMs float64 `json:"state_update_ms"` StateUpdateMs float64 `json:"state_update_ms"`
// StateHashMs is state-root hash computation.
StateHashMs float64 `json:"state_hash_ms"` StateHashMs float64 `json:"state_hash_ms"`
// AccountCommitMs is the account trie commit to disk.
AccountCommitMs float64 `json:"account_commit_ms"` AccountCommitMs float64 `json:"account_commit_ms"`
// StorageCommitMs is the storage trie commit to disk.
StorageCommitMs float64 `json:"storage_commit_ms"` StorageCommitMs float64 `json:"storage_commit_ms"`
// TrieDBCommitMs is the trie database commit.
TrieDBCommitMs float64 `json:"triedb_commit_ms"` TrieDBCommitMs float64 `json:"triedb_commit_ms"`
// SnapshotCommitMs is the state snapshot commit.
SnapshotCommitMs float64 `json:"snapshot_commit_ms"` SnapshotCommitMs float64 `json:"snapshot_commit_ms"`
} }
@ -221,9 +201,8 @@ func durationToMs(d time.Duration) float64 {
return float64(d.Nanoseconds()) / 1e6 return float64(d.Nanoseconds()) / 1e6
} }
// buildSlowBlockLog constructs the slow-block log JSON struct from execution // buildSlowBlockLog builds the slow-block JSON payload. Split out from logSlow
// statistics. Pure function — no side effects, no logging — to make the JSON // so the JSON shape is directly testable.
// shape directly testable.
func buildSlowBlockLog(s *ExecuteStats, block *types.Block) slowBlockLog { func buildSlowBlockLog(s *ExecuteStats, block *types.Block) slowBlockLog {
logEntry := slowBlockLog{ logEntry := slowBlockLog{
Level: "warn", Level: "warn",
@ -278,7 +257,6 @@ func buildSlowBlockLog(s *ExecuteStats, block *types.Block) slowBlockLog {
}, },
}, },
} }
// Populate the parallel-execution extension only for BAL-processed blocks.
if m := s.balTransitionStats; m != nil { if m := s.balTransitionStats; m != nil {
logEntry.BAL = &slowBlockBAL{ logEntry.BAL = &slowBlockBAL{
ExecWallMs: durationToMs(s.ExecWall), ExecWallMs: durationToMs(s.ExecWall),

View file

@ -24,17 +24,14 @@ type ProcessResultWithMetrics struct {
// the time it took to execute all txs in the block // the time it took to execute all txs in the block
ExecTime time.Duration ExecTime time.Duration
PostProcessTime time.Duration PostProcessTime time.Duration
// Counts is the per-StateDB sum of state-mutation counters across pre-tx, // Counts sums state-mutation counters across pre-tx, per-tx and post-tx
// per-tx and post-tx phases. The caller may override AccountLoaded and // StateDBs. AccountLoaded/StorageLoaded are NOT deduplicated here — the
// StorageLoaded with deduplicated counts derived from block.AccessList() // caller overrides them from block.AccessList().
// (per-StateDB sums over-count addresses touched by multiple phases).
Counts state.StateCounts Counts state.StateCounts
// Reads is the sum of per-StateDB read times across pre-tx, per-tx and // Reads sums per-StateDB read times (sum-of-CPU-time, not wall-clock).
// post-tx phases. Sum-of-CPU-time, not wall-clock.
Reads state.ReadDurations Reads state.ReadDurations
// CodeLoaded is the deduplicated count of unique contract addresses whose // CodeLoaded/CodeLoadBytes are deduplicated by contract address across
// code body was fetched during the block (across all phase StateDBs). // all phase StateDBs.
// CodeLoadBytes is the sum of those code lengths.
CodeLoaded int CodeLoaded int
CodeLoadBytes int CodeLoadBytes int
} }
@ -184,9 +181,7 @@ func (p *ParallelStateProcessor) prepareExecResult(block *types.Block, tExecStar
tPostprocess := time.Since(tPostprocessStart) tPostprocess := time.Since(tPostprocessStart)
// Fold post-tx statedb counts and reads into the aggregate. postTxState is // Fold post-tx counts/reads in: postTxState is local and otherwise discarded.
// local and would otherwise be discarded; this captures system-contract
// activity (withdrawal queue, consolidation queue) and engine.Finalize.
aggCounts.Add(postTxState.SnapshotCounts()) aggCounts.Add(postTxState.SnapshotCounts())
aggReads.Add(postTxState.SnapshotReads()) aggReads.Add(postTxState.SnapshotReads())
for addr, l := range postTxState.SnapshotCodeLoads() { for addr, l := range postTxState.SnapshotCodeLoads() {
@ -230,14 +225,10 @@ type txExecResult struct {
stateReads bal.StateAccesses stateReads bal.StateAccesses
// Per-tx state-mutation counts and read durations, snapshotted from the // Per-tx counts/reads/code-loads, aggregated single-threaded in resultHandler.
// worker statedb just before send. Aggregated single-threaded in
// resultHandler.
counts state.StateCounts counts state.StateCounts
reads state.ReadDurations reads state.ReadDurations
// codeLoads is addr→codeLen for contracts whose code body was fetched codeLoads map[common.Address]int // addr → code len, deduped across phases
// in this tx. Deduped across all phases in resultHandler.
codeLoads map[common.Address]int
} }
// resultHandler polls until all transactions have finished executing and the // resultHandler polls until all transactions have finished executing and the
@ -251,13 +242,9 @@ func (p *ParallelStateProcessor) resultHandler(block *types.Block, preTxAccesses
var numTxComplete int var numTxComplete int
// Seed aggregates with the pre-tx contribution (BeaconRoot, ParentBlockHash). // Seed aggregates with the pre-tx contribution (BeaconRoot, ParentBlockHash).
// Per-tx fold below; post-tx fold in prepareExecResult.
accesses := preTxAccesses accesses := preTxAccesses
aggCounts := preCounts aggCounts := preCounts
aggReads := preReads aggReads := preReads
// Dedup'd map of contract addresses whose code body was fetched by any
// phase StateDB. Address-keyed so multiple phases adding the same contract
// only count it once.
aggCodeLoads := make(map[common.Address]int) aggCodeLoads := make(map[common.Address]int)
for addr, l := range preCodeLoads { for addr, l := range preCodeLoads {
aggCodeLoads[addr] = l aggCodeLoads[addr] = l
@ -406,9 +393,7 @@ func (p *ParallelStateProcessor) processBlockPreTx(block *types.Block, statedb *
if !accessList.MutationsAt(0).Eq(mutations) { if !accessList.MutationsAt(0).Eq(mutations) {
return nil, state.StateCounts{}, state.ReadDurations{}, nil, fmt.Errorf("invalid block access list: mismatch between local/remote access list mutations at idx 0") return nil, state.StateCounts{}, state.ReadDurations{}, nil, fmt.Errorf("invalid block access list: mismatch between local/remote access list mutations at idx 0")
} }
// Snapshot the pre-tx statedb's counts/reads/code-loads so system-contract // Snapshot pre-tx counts/reads/code-loads: sdb is local and otherwise discarded.
// activity (BeaconRoot, ParentBlockHash) contributes to the aggregate;
// sdb is local and would otherwise be discarded.
return reads, sdb.SnapshotCounts(), sdb.SnapshotReads(), sdb.SnapshotCodeLoads(), nil return reads, sdb.SnapshotCounts(), sdb.SnapshotReads(), sdb.SnapshotCodeLoads(), nil
} }

View file

@ -41,18 +41,14 @@ type BALStateTransition struct {
tries sync.Map //map[common.Address]Trie tries sync.Map //map[common.Address]Trie
deletions map[common.Address]struct{} deletions map[common.Address]struct{}
// Storage counters use atomic.Int64 because they're written from per-address // Storage/read counters are atomic — written from per-address goroutines.
// goroutines. The others are written single-threaded inside IntermediateRoot's // account/code counters are plain int — written single-threaded.
// serial mutation loop, so plain int matches StateCounts' int fields.
accountDeleted int accountDeleted int
accountUpdated int accountUpdated int
storageDeleted atomic.Int64 storageDeleted atomic.Int64
storageUpdated atomic.Int64 storageUpdated atomic.Int64
codeUpdated int codeUpdated int
codeUpdateBytes int codeUpdateBytes int
// Read-time accumulators for state-root recomputation reads. Atomic
// because s.reader.Account/Storage is called from per-address goroutines.
accountReadNS atomic.Int64 accountReadNS atomic.Int64
storageReadNS atomic.Int64 storageReadNS atomic.Int64
@ -64,22 +60,16 @@ type BALStateTransition struct {
err error err error
} }
// Metrics returns the cached commit/hash-phase timings. Read-time atomics
// are exposed separately via ReadTimes; that decoupling avoids the
// snapshot-staleness pitfall when commitAccount runs more reads after
// Metrics is first called.
func (s *BALStateTransition) Metrics() *BALStateTransitionMetrics { func (s *BALStateTransition) Metrics() *BALStateTransitionMetrics {
return &s.metrics return &s.metrics
} }
// ReadTimes returns the current accumulated read times from atomic counters. // ReadTimes returns the accumulated state-read times.
// Always live; safe to call at any point after IntermediateRoot/Commit work.
func (s *BALStateTransition) ReadTimes() (account, storage time.Duration) { func (s *BALStateTransition) ReadTimes() (account, storage time.Duration) {
return time.Duration(s.accountReadNS.Load()), time.Duration(s.storageReadNS.Load()) return time.Duration(s.accountReadNS.Load()), time.Duration(s.storageReadNS.Load())
} }
// WriteCounts returns the state-mutation counts tracked during the parallel // WriteCounts returns the state-mutation counts from the parallel state-root pass.
// state-root computation.
func (s *BALStateTransition) WriteCounts() StateCounts { func (s *BALStateTransition) WriteCounts() StateCounts {
return StateCounts{ return StateCounts{
AccountUpdated: s.accountUpdated, AccountUpdated: s.accountUpdated,
@ -523,11 +513,9 @@ func (s *BALStateTransition) IntermediateRoot(_ bool) common.Hash {
} else { } else {
acct, code := s.updateAccount(mutatedAddr) acct, code := s.updateAccount(mutatedAddr)
// Use len(code) > 0 (not code != nil) to match the non-BAL semantic // Empty []byte is non-nil but means "no code install" in devnet-3
// at statedb.go (obj.dirtyCode && len(obj.code) > 0). In devnet-3 // BAL access lists; matches the obj.dirtyCode && len(obj.code) > 0
// BAL access lists, an empty []byte is non-nil but encodes "no code // gate in statedb.go.
// install"; treating it as a code mutation would over-count and
// call UpdateContractCode with an empty payload.
if len(code) > 0 { if len(code) > 0 {
codeHash := crypto.Keccak256Hash(code) codeHash := crypto.Keccak256Hash(code)
acct.CodeHash = codeHash.Bytes() acct.CodeHash = codeHash.Bytes()

View file

@ -565,9 +565,7 @@ func (r *reader) GetStats() ReaderStats {
} }
} }
// PrefetchReadTimes returns the prefetcher's accumulated read times if the // PrefetchReadTimes forwards to the wrapped prefetcher, or returns zero.
// underlying state reader exposes them (e.g. *prefetchStateReader). Returns
// zero if the wrapped reader doesn't track these (sequential paths, tests).
func (r *reader) PrefetchReadTimes() (account, storage time.Duration) { func (r *reader) PrefetchReadTimes() (account, storage time.Duration) {
if pr, ok := r.StateReader.(interface { if pr, ok := r.StateReader.(interface {
PrefetchReadTimes() (time.Duration, time.Duration) PrefetchReadTimes() (time.Duration, time.Duration)
@ -577,8 +575,7 @@ func (r *reader) PrefetchReadTimes() (account, storage time.Duration) {
return 0, 0 return 0, 0
} }
// WaitPrefetch blocks until the wrapped prefetcher (if any) finishes its // WaitPrefetch blocks until the wrapped prefetcher drains; no-op otherwise.
// task list. No-op for non-prefetch readers.
func (r *reader) WaitPrefetch() { func (r *reader) WaitPrefetch() {
if pr, ok := r.StateReader.(interface{ Wait() error }); ok { if pr, ok := r.StateReader.(interface{ Wait() error }); ok {
_ = pr.Wait() _ = pr.Wait()

View file

@ -89,8 +89,7 @@ type prefetchStateReader struct {
term chan struct{} term chan struct{}
closeOnce sync.Once closeOnce sync.Once
// Async-fetch read-time accumulators (atomic because process() runs // Atomic — process() runs across N goroutines.
// across N goroutines).
accountReadNS atomic.Int64 accountReadNS atomic.Int64
storageReadNS atomic.Int64 storageReadNS atomic.Int64
} }
@ -374,9 +373,8 @@ func (r *readerTracker) TouchStorage(addr common.Address, slot common.Hash) {
list[slot] = struct{}{} list[slot] = struct{}{}
} }
// GetStateStats forwards stats from the wrapped *stateReaderWithStats so the // GetStateStats forwards stats from the wrapped reader; without this, BAL
// (*reader).GetStateStats type assertion succeeds. Without this, account/ // blocks would emit zero cache hit/miss counts.
// storage cache hit/miss counts emit zero on BAL blocks.
func (r *prefetchStateReader) GetStateStats() StateReaderStats { func (r *prefetchStateReader) GetStateStats() StateReaderStats {
if stater, ok := r.StateReader.(StateReaderStater); ok { if stater, ok := r.StateReader.(StateReaderStater); ok {
return stater.GetStateStats() return stater.GetStateStats()
@ -384,9 +382,8 @@ func (r *prefetchStateReader) GetStateStats() StateReaderStats {
return StateReaderStats{} return StateReaderStats{}
} }
// PrefetchReadTimes returns the accumulated wall-time-of-each-call durations // PrefetchReadTimes returns sum-of-CPU-time across worker goroutines (not
// for asynchronous account/storage prefetches. Sum-of-CPU-time across worker // wall-clock).
// goroutines; not wall-clock total prefetch time.
func (r *prefetchStateReader) PrefetchReadTimes() (account, storage time.Duration) { func (r *prefetchStateReader) PrefetchReadTimes() (account, storage time.Duration) {
return time.Duration(r.accountReadNS.Load()), time.Duration(r.storageReadNS.Load()) return time.Duration(r.accountReadNS.Load()), time.Duration(r.storageReadNS.Load())
} }

View file

@ -291,23 +291,17 @@ func TestPrefetchStateReaderForwardsStats(t *testing.T) {
} }
} }
// TestReaderForwardsPrefetchReadTimes locks down that the *reader aggregator // TestReaderForwardsPrefetchReadTimes locks down that *reader exposes the
// (the type returned by ReaderEIP7928) exposes PrefetchReadTimes via the // inner prefetcher's read-time counters via PrefetchReadTimes.
// inner *prefetchStateReader. Without the forwarding method on *reader,
// callers that hold a Reader interface would not see the prefetcher's
// accumulated read times even though the prefetcher tracks them.
func TestReaderForwardsPrefetchReadTimes(t *testing.T) { func TestReaderForwardsPrefetchReadTimes(t *testing.T) {
stub := newRefStateReader() stub := newRefStateReader()
cached := newStateReaderWithCache(stub) cached := newStateReaderWithCache(stub)
withStats := newStateReaderWithStats(cached) withStats := newStateReaderWithStats(cached)
prefetch := newPrefetchStateReaderInternal(withStats, nil, 1) prefetch := newPrefetchStateReaderInternal(withStats, nil, 1)
// Seed timer values directly on the prefetcher.
prefetch.accountReadNS.Store(123) prefetch.accountReadNS.Store(123)
prefetch.storageReadNS.Store(456) prefetch.storageReadNS.Store(456)
// Wrap in *reader the way ReaderEIP7928 does (with a nil code reader for
// brevity; PrefetchReadTimes only inspects the state side).
r := newReader(nil, prefetch) r := newReader(nil, prefetch)
a, s := r.PrefetchReadTimes() a, s := r.PrefetchReadTimes()
@ -319,8 +313,8 @@ func TestReaderForwardsPrefetchReadTimes(t *testing.T) {
} }
} }
// TestReaderPrefetchReadTimesNonPrefetch verifies the safe zero fallback when // TestReaderPrefetchReadTimesNonPrefetch verifies the zero fallback when the
// the wrapped state reader doesn't expose PrefetchReadTimes (sequential path). // wrapped reader doesn't expose PrefetchReadTimes.
func TestReaderPrefetchReadTimesNonPrefetch(t *testing.T) { func TestReaderPrefetchReadTimesNonPrefetch(t *testing.T) {
r := newReader(nil, newRefStateReader()) r := newReader(nil, newRefStateReader())
a, s := r.PrefetchReadTimes() a, s := r.PrefetchReadTimes()

View file

@ -223,9 +223,8 @@ func (s *StateDB) WithReader(reader Reader) *StateDB {
return cpy return cpy
} }
// ReadDurations groups the {Account, Storage, Code} state-read times that are // ReadDurations groups the {Account, Storage, Code} state-read times.
// aggregated across pre-tx, per-tx and post-tx statedbs in the BAL parallel // Sum-of-CPU-time when aggregated across BAL phase statedbs.
// path. Sum-of-CPU-time, not wall-clock.
type ReadDurations struct { type ReadDurations struct {
Account time.Duration Account time.Duration
Storage time.Duration Storage time.Duration
@ -239,10 +238,8 @@ func (r *ReadDurations) Add(other ReadDurations) {
r.Code += other.Code r.Code += other.Code
} }
// StateCounts holds count-only statistics gathered during a block's state // StateCounts is a plain-int snapshot of state-mutation counters. Atomic
// transition. Plain-int snapshot type, safe to copy through channels. // fields on StateDB are Load()'d at the SnapshotCounts boundary.
// Atomic counters on StateDB are converted at the snapshot boundary in
// SnapshotCounts. Read durations live in ReadDurations (separate type).
type StateCounts struct { type StateCounts struct {
AccountLoaded int // accounts retrieved from the database during the state transition AccountLoaded int // accounts retrieved from the database during the state transition
AccountUpdated int // accounts updated during the state transition AccountUpdated int // accounts updated during the state transition
@ -256,10 +253,7 @@ type StateCounts struct {
CodeUpdateBytes int // total bytes of persisted code written CodeUpdateBytes int // total bytes of persisted code written
} }
// Add merges other into c. Plain integer addition — no atomics here, since // Add merges other into c.
// StateCounts is the snapshot type. The receiver is the only mutated party;
// other is taken by value (the struct is small and value semantics matches
// the snapshot thesis stated above).
func (c *StateCounts) Add(other StateCounts) { func (c *StateCounts) Add(other StateCounts) {
c.AccountLoaded += other.AccountLoaded c.AccountLoaded += other.AccountLoaded
c.AccountUpdated += other.AccountUpdated c.AccountUpdated += other.AccountUpdated
@ -273,9 +267,7 @@ func (c *StateCounts) Add(other StateCounts) {
c.CodeUpdateBytes += other.CodeUpdateBytes c.CodeUpdateBytes += other.CodeUpdateBytes
} }
// SnapshotCounts returns a value-copy of the state-mutation counters as a // SnapshotCounts returns a plain-int copy of the state-mutation counters.
// plain-int StateCounts. Atomic fields are read via Load(); the result is
// safe to copy, pass through channels, and aggregate via StateCounts.Add.
func (s *StateDB) SnapshotCounts() StateCounts { func (s *StateDB) SnapshotCounts() StateCounts {
return StateCounts{ return StateCounts{
AccountLoaded: s.AccountLoaded, AccountLoaded: s.AccountLoaded,
@ -291,8 +283,7 @@ func (s *StateDB) SnapshotCounts() StateCounts {
} }
} }
// SnapshotReads returns a value-copy of the {Account, Storage, Code} read // SnapshotReads returns the {Account, Storage, Code} read durations.
// durations accumulated on this StateDB.
func (s *StateDB) SnapshotReads() ReadDurations { func (s *StateDB) SnapshotReads() ReadDurations {
return ReadDurations{ return ReadDurations{
Account: s.AccountReads, Account: s.AccountReads,
@ -301,9 +292,8 @@ func (s *StateDB) SnapshotReads() ReadDurations {
} }
} }
// SnapshotCodeLoads returns the addresses whose contract code body was // SnapshotCodeLoads returns addresses whose code body was fetched, mapped to
// fetched during this StateDB's lifetime, mapped to byte length. Used by the // byte length. Used to deduplicate code-load events across BAL phase StateDBs.
// BAL parallel pipeline to deduplicate code-load events across phase StateDBs.
func (s *StateDB) SnapshotCodeLoads() map[common.Address]int { func (s *StateDB) SnapshotCodeLoads() map[common.Address]int {
if len(s.stateObjects) == 0 { if len(s.stateObjects) == 0 {
return nil return nil