From d781223b81c4e344467e50cda172b7968bd08e52 Mon Sep 17 00:00:00 2001 From: Adrian Sutton Date: Tue, 27 Aug 2024 13:39:36 +1000 Subject: [PATCH] Support purely single threaded execution (#370) When only a single CPU is available: * Avoid creating threads in StateDB.IntermediateRoot * Force single threaded hashing --- core/state/statedb.go | 11 +++++------ core/state/workers.go | 38 ++++++++++++++++++++++++++++++++++++++ fork.yaml | 7 +++++++ trie/hasher.go | 3 ++- 4 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 core/state/workers.go diff --git a/core/state/statedb.go b/core/state/statedb.go index 74f2e9518a..bbd4eba339 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -42,7 +42,6 @@ import ( "github.com/ethereum/go-ethereum/trie/triestate" "github.com/ethereum/go-ethereum/trie/utils" "github.com/holiman/uint256" - "golang.org/x/sync/errgroup" ) // TriesInMemory represents the number of layers that are kept in RAM. @@ -851,9 +850,9 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash { // method will internally call a blocking trie fetch from the prefetcher, // so there's no need to explicitly wait for the prefetchers to finish. var ( - start = time.Now() - workers errgroup.Group + start = time.Now() ) + workers := newWorkerGroup() if s.db.TrieDB().IsVerkle() { // Whilst MPT storage tries are independent, Verkle has one single trie // for all the accounts and all the storage slots merged together. The @@ -1225,10 +1224,10 @@ func (s *StateDB) commit(deleteEmptyObjects bool) (*stateUpdate, error) { // off some milliseconds from the commit operation. Also accumulate the code // writes to run in parallel with the computations. var ( - start = time.Now() - root common.Hash - workers errgroup.Group + start = time.Now() + root common.Hash ) + workers := newWorkerGroup() // Schedule the account trie first since that will be the biggest, so give // it the most time to crunch. // diff --git a/core/state/workers.go b/core/state/workers.go new file mode 100644 index 0000000000..d145cfe8a5 --- /dev/null +++ b/core/state/workers.go @@ -0,0 +1,38 @@ +package state + +import ( + "errors" + "runtime" + + "golang.org/x/sync/errgroup" +) + +type workerGroup interface { + Go(func() error) + SetLimit(int) + Wait() error +} + +func newWorkerGroup() workerGroup { + if runtime.NumCPU() <= 1 { + return &inlineWorkerGroup{} + } else { + var grp errgroup.Group + return &grp + } +} + +type inlineWorkerGroup struct { + err error +} + +func (i *inlineWorkerGroup) Go(action func() error) { + i.err = errors.Join(i.err, action()) +} + +func (i *inlineWorkerGroup) SetLimit(_ int) { +} + +func (i *inlineWorkerGroup) Wait() error { + return i.err +} diff --git a/fork.yaml b/fork.yaml index 1149e18c35..134b463ca3 100644 --- a/fork.yaml +++ b/fork.yaml @@ -218,6 +218,13 @@ def: See upstream Geth PR 28940, and op-geth PR 368 for details. globs: - "triedb/pathdb/journal.go" + - title: "Single threaded execution" + description: | + The cannon fault proofs virtual machine does not support the creation of threads. To ensure compatibility, + thread creation is avoided when only a single CPU is available. + globs: + - "core/state/workers.go" + - "trie/hasher.go" - title: "User API enhancements" description: "Encode the Deposit Tx properties, the L1 costs, and daisy-chain RPC-calls for pre-Bedrock historical data" sub: diff --git a/trie/hasher.go b/trie/hasher.go index abf654c709..9b19abbe6f 100644 --- a/trie/hasher.go +++ b/trie/hasher.go @@ -17,6 +17,7 @@ package trie import ( + "runtime" "sync" "github.com/ethereum/go-ethereum/crypto" @@ -45,7 +46,7 @@ var hasherPool = sync.Pool{ func newHasher(parallel bool) *hasher { h := hasherPool.Get().(*hasher) - h.parallel = parallel + h.parallel = parallel && runtime.NumCPU() > 1 return h }