From 9795a287d00b8ffe9400ac7f9606cb339faca51f Mon Sep 17 00:00:00 2001 From: Pratik Patil Date: Thu, 9 Feb 2023 13:34:06 +0530 Subject: [PATCH] added 2 flags to enable parallel EVM and set the number of speculative processes (#727) --- builder/files/config.toml | 4 ++++ cmd/utils/flags.go | 1 + core/blockstm/executor.go | 15 ++++++++++----- core/parallel_state_processor.go | 7 +++++++ core/vm/interpreter.go | 4 ++++ docs/config.md | 4 ++++ eth/backend.go | 14 ++++++++++++-- eth/ethconfig/config.go | 3 +++ internal/cli/server/config.go | 16 ++++++++++++++++ internal/cli/server/flags.go | 14 ++++++++++++++ miner/worker_test.go | 2 +- tests/block_test_util.go | 17 +++++++++-------- 12 files changed, 85 insertions(+), 16 deletions(-) diff --git a/builder/files/config.toml b/builder/files/config.toml index 870c164a8d..1dea17c170 100644 --- a/builder/files/config.toml +++ b/builder/files/config.toml @@ -136,3 +136,7 @@ syncmode = "full" # [developer] # dev = false # period = 0 + +# [parallelevm] + # enable = false + # procs = 8 \ No newline at end of file diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 81ce27ef4c..71c4cc0cd9 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -2081,6 +2081,7 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai // TODO(rjl493456442) disable snapshot generation/wiping if the chain is read only. // Disable transaction indexing/unindexing by default. + // PSP - Check for config.ParallelEVM.Enable here? chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg, nil, nil, nil) if err != nil { Fatalf("Can't create BlockChain: %v", err) diff --git a/core/blockstm/executor.go b/core/blockstm/executor.go index a086347610..37bde617e5 100644 --- a/core/blockstm/executor.go +++ b/core/blockstm/executor.go @@ -36,6 +36,12 @@ type ExecVersionView struct { sender common.Address } +var NumSpeculativeProcs int = 8 + +func SetProcs(specProcs int) { + NumSpeculativeProcs = specProcs +} + func (ev *ExecVersionView) Execute() (er ExecResult) { er.ver = ev.ver if er.err = ev.et.Execute(ev.mvh, ev.ver.Incarnation); er.err != nil { @@ -157,8 +163,7 @@ type ParallelExecutionResult struct { AllDeps map[int]map[int]bool } -const numGoProcs = 2 -const numSpeculativeProcs = 8 +const numGoProcs = 1 type ParallelExecutor struct { tasks []ExecTask @@ -315,10 +320,10 @@ func (pe *ParallelExecutor) Prepare() { } } - pe.workerWg.Add(numSpeculativeProcs + numGoProcs) + pe.workerWg.Add(NumSpeculativeProcs + numGoProcs) // Launch workers that execute transactions - for i := 0; i < numSpeculativeProcs+numGoProcs; i++ { + for i := 0; i < NumSpeculativeProcs+numGoProcs; i++ { go func(procNum int) { defer pe.workerWg.Done() @@ -352,7 +357,7 @@ func (pe *ParallelExecutor) Prepare() { } } - if procNum < numSpeculativeProcs { + if procNum < NumSpeculativeProcs { for range pe.chSpeculativeTasks { doWork(pe.specTaskQueue.Pop().(ExecVersionView)) } diff --git a/core/parallel_state_processor.go b/core/parallel_state_processor.go index 23457d5c60..0c97d074ff 100644 --- a/core/parallel_state_processor.go +++ b/core/parallel_state_processor.go @@ -34,6 +34,11 @@ import ( "github.com/ethereum/go-ethereum/params" ) +type ParallelEVMConfig struct { + Enable bool + SpeculativeProcesses int +} + // StateProcessor is a basic Processor, which takes care of transitioning // state from one point to another. // @@ -269,6 +274,8 @@ var parallelizabilityTimer = metrics.NewRegisteredTimer("block/parallelizability // transactions failed to execute due to insufficient gas it will return an error. // nolint:gocognit func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) { + blockstm.SetProcs(cfg.ParallelSpeculativeProcesses) + var ( receipts types.Receipts header = block.Header() diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 21e3c914e1..554a3dc96f 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -34,6 +34,10 @@ type Config struct { JumpTable *JumpTable // EVM instruction table, automatically populated if unset ExtraEips []int // Additional EIPS that are to be enabled + + // parallel EVM configs + ParallelEnable bool + ParallelSpeculativeProcesses int } // ScopeContext contains the things that are per-call, such as stack and memory, diff --git a/docs/config.md b/docs/config.md index 57f4c25fef..ebec217b96 100644 --- a/docs/config.md +++ b/docs/config.md @@ -143,4 +143,8 @@ addr = ":3131" [developer] dev = false period = 0 + +[blockstm] +enable = false +procs = 8 ``` diff --git a/eth/backend.go b/eth/backend.go index 824fec8914..ca8a1759a9 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -108,6 +108,7 @@ type Ethereum struct { shutdownTracker *shutdowncheck.ShutdownTracker // Tracks if and when the node has shutdown ungracefully } +// PSP // New creates a new Ethereum object (including the // initialisation of the common Ethereum object) func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { @@ -206,7 +207,9 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { } var ( vmConfig = vm.Config{ - EnablePreimageRecording: config.EnablePreimageRecording, + EnablePreimageRecording: config.EnablePreimageRecording, + ParallelEnable: config.ParallelEVM.Enable, + ParallelSpeculativeProcesses: config.ParallelEVM.SpeculativeProcesses, } cacheConfig = &core.CacheConfig{ TrieCleanLimit: config.TrieCleanCache, @@ -224,7 +227,14 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { checker := whitelist.NewService(10) - eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, checker) + // check if Parallel EVM is enabled + // if enabled, use parallel state processor + if config.ParallelEVM.Enable { + eth.blockchain, err = core.NewParallelBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, checker) + } else { + eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, chainConfig, eth.engine, vmConfig, eth.shouldPreserve, &config.TxLookupLimit, checker) + } + if err != nil { return nil, err } diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go index c9272758ab..1353cc488b 100644 --- a/eth/ethconfig/config.go +++ b/eth/ethconfig/config.go @@ -224,6 +224,9 @@ type Config struct { // Bor logs flag BorLogs bool + // Parallel EVM (Block-STM) related config + ParallelEVM core.ParallelEVMConfig `toml:",omitempty"` + // Arrow Glacier block override (TODO: remove after the fork) OverrideArrowGlacier *big.Int `toml:",omitempty"` diff --git a/internal/cli/server/config.go b/internal/cli/server/config.go index 34c17b3f7d..d8b0d8f822 100644 --- a/internal/cli/server/config.go +++ b/internal/cli/server/config.go @@ -104,6 +104,9 @@ type Config struct { // Developer has the developer mode related settings Developer *DeveloperConfig `hcl:"developer,block" toml:"developer,block"` + + // ParallelEVM has the parallel evm related settings + ParallelEVM *ParallelEVMConfig `hcl:"parallelevm,block" toml:"parallelevm,block"` } type P2PConfig struct { @@ -398,6 +401,12 @@ type DeveloperConfig struct { Period uint64 `hcl:"period,optional" toml:"period,optional"` } +type ParallelEVMConfig struct { + Enable bool `hcl:"enable,optional" toml:"enable,optional"` + + SpeculativeProcesses int `hcl:"procs,optional" toml:"procs,optional"` +} + func DefaultConfig() *Config { return &Config{ Chain: "mainnet", @@ -531,6 +540,10 @@ func DefaultConfig() *Config { Enabled: false, Period: 0, }, + ParallelEVM: &ParallelEVMConfig{ + Enable: false, + SpeculativeProcesses: 8, + }, } } @@ -893,6 +906,9 @@ func (c *Config) buildEth(stack *node.Node, accountManager *accounts.Manager) (* n.BorLogs = c.BorLogs n.DatabaseHandles = dbHandles + n.ParallelEVM.Enable = c.ParallelEVM.Enable + n.ParallelEVM.SpeculativeProcesses = c.ParallelEVM.SpeculativeProcesses + return &n, nil } diff --git a/internal/cli/server/flags.go b/internal/cli/server/flags.go index ba9be13376..5467713ab2 100644 --- a/internal/cli/server/flags.go +++ b/internal/cli/server/flags.go @@ -682,5 +682,19 @@ func (c *Command) Flags() *flagset.Flagset { Value: &c.cliConfig.Developer.Period, Default: c.cliConfig.Developer.Period, }) + + // parallelevm + f.BoolFlag(&flagset.BoolFlag{ + Name: "parallelevm.enable", + Usage: "Enable Block STM", + Value: &c.cliConfig.ParallelEVM.Enable, + Default: c.cliConfig.ParallelEVM.Enable, + }) + f.IntFlag(&flagset.IntFlag{ + Name: "parallelevm.procs", + Usage: "Number of speculative processes (cores) in Block STM", + Value: &c.cliConfig.ParallelEVM.SpeculativeProcesses, + Default: c.cliConfig.ParallelEVM.SpeculativeProcesses, + }) return f } diff --git a/miner/worker_test.go b/miner/worker_test.go index f99e6ae706..3306ad4069 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -756,7 +756,7 @@ func BenchmarkBorMiningBlockSTMMetadata(b *testing.B) { db2 := rawdb.NewMemoryDatabase() back.Genesis.MustCommit(db2) - chain, _ := core.NewParallelBlockChain(db2, nil, back.chain.Config(), engine, vm.Config{}, nil, nil, nil) + chain, _ := core.NewParallelBlockChain(db2, nil, back.chain.Config(), engine, vm.Config{ParallelEnable: true, ParallelSpeculativeProcesses: 8}, nil, nil, nil) defer chain.Stop() // Ignore empty commit here for less noise. diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 487fd2d4d8..64b9008fe3 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -176,17 +176,18 @@ func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis { } } -/* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II +/* +See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II - Whether a block is valid or not is a bit subtle, it's defined by presence of - blockHeader, transactions and uncleHeaders fields. If they are missing, the block is - invalid and we must verify that we do not accept it. + Whether a block is valid or not is a bit subtle, it's defined by presence of + blockHeader, transactions and uncleHeaders fields. If they are missing, the block is + invalid and we must verify that we do not accept it. - Since some tests mix valid and invalid blocks we need to check this for every block. + Since some tests mix valid and invalid blocks we need to check this for every block. - If a block is invalid it does not necessarily fail the test, if it's invalidness is - expected we are expected to ignore it and continue processing and then validate the - post state. + If a block is invalid it does not necessarily fail the test, if it's invalidness is + expected we are expected to ignore it and continue processing and then validate the + post state. */ func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) { validBlocks := make([]btBlock, 0)