Move NumSpeculativeProcs from module variable to function parameter (#931)

This will prevent data races when more than one parallel execution are running at the same time.
This commit is contained in:
Jerry 2023-07-13 12:56:55 +08:00 committed by GitHub
parent 7e0e4c8905
commit eedeaed1fb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 43 deletions

View file

@ -37,12 +37,6 @@ type ExecVersionView struct {
sender common.Address sender common.Address
} }
var NumSpeculativeProcs int = 8
func SetProcs(specProcs int) {
NumSpeculativeProcs = specProcs
}
func (ev *ExecVersionView) Execute() (er ExecResult) { func (ev *ExecVersionView) Execute() (er ExecResult) {
er.ver = ev.ver er.ver = ev.ver
if er.err = ev.et.Execute(ev.mvh, ev.ver.Incarnation); er.err != nil { if er.err = ev.et.Execute(ev.mvh, ev.ver.Incarnation); er.err != nil {
@ -180,6 +174,9 @@ type ParallelExecutor struct {
// Stores the execution statistics for the last incarnation of each task // Stores the execution statistics for the last incarnation of each task
stats map[int]ExecutionStat stats map[int]ExecutionStat
// Number of workers that execute transactions speculatively
numSpeculativeProcs int
statsMutex sync.Mutex statsMutex sync.Mutex
// Channel for tasks that should be prioritized // Channel for tasks that should be prioritized
@ -255,7 +252,7 @@ type ExecutionStat struct {
Worker int Worker int
} }
func NewParallelExecutor(tasks []ExecTask, profile bool, metadata bool) *ParallelExecutor { func NewParallelExecutor(tasks []ExecTask, profile bool, metadata bool, numProcs int) *ParallelExecutor {
numTasks := len(tasks) numTasks := len(tasks)
var resultQueue SafeQueue var resultQueue SafeQueue
@ -271,27 +268,28 @@ func NewParallelExecutor(tasks []ExecTask, profile bool, metadata bool) *Paralle
} }
pe := &ParallelExecutor{ pe := &ParallelExecutor{
tasks: tasks, tasks: tasks,
stats: make(map[int]ExecutionStat, numTasks), numSpeculativeProcs: numProcs,
chTasks: make(chan ExecVersionView, numTasks), stats: make(map[int]ExecutionStat, numTasks),
chSpeculativeTasks: make(chan struct{}, numTasks), chTasks: make(chan ExecVersionView, numTasks),
chSettle: make(chan int, numTasks), chSpeculativeTasks: make(chan struct{}, numTasks),
chResults: make(chan struct{}, numTasks), chSettle: make(chan int, numTasks),
specTaskQueue: specTaskQueue, chResults: make(chan struct{}, numTasks),
resultQueue: resultQueue, specTaskQueue: specTaskQueue,
lastSettled: -1, resultQueue: resultQueue,
skipCheck: make(map[int]bool), lastSettled: -1,
execTasks: makeStatusManager(numTasks), skipCheck: make(map[int]bool),
validateTasks: makeStatusManager(0), execTasks: makeStatusManager(numTasks),
diagExecSuccess: make([]int, numTasks), validateTasks: makeStatusManager(0),
diagExecAbort: make([]int, numTasks), diagExecSuccess: make([]int, numTasks),
mvh: MakeMVHashMap(), diagExecAbort: make([]int, numTasks),
lastTxIO: MakeTxnInputOutput(numTasks), mvh: MakeMVHashMap(),
txIncarnations: make([]int, numTasks), lastTxIO: MakeTxnInputOutput(numTasks),
estimateDeps: make(map[int][]int), txIncarnations: make([]int, numTasks),
preValidated: make(map[int]bool), estimateDeps: make(map[int][]int),
begin: time.Now(), preValidated: make(map[int]bool),
profile: profile, begin: time.Now(),
profile: profile,
} }
return pe return pe
@ -329,10 +327,10 @@ func (pe *ParallelExecutor) Prepare() error {
} }
} }
pe.workerWg.Add(NumSpeculativeProcs + numGoProcs) pe.workerWg.Add(pe.numSpeculativeProcs + numGoProcs)
// Launch workers that execute transactions // Launch workers that execute transactions
for i := 0; i < NumSpeculativeProcs+numGoProcs; i++ { for i := 0; i < pe.numSpeculativeProcs+numGoProcs; i++ {
go func(procNum int) { go func(procNum int) {
defer pe.workerWg.Done() defer pe.workerWg.Done()
@ -366,7 +364,7 @@ func (pe *ParallelExecutor) Prepare() error {
} }
} }
if procNum < NumSpeculativeProcs { if procNum < pe.numSpeculativeProcs {
for range pe.chSpeculativeTasks { for range pe.chSpeculativeTasks {
doWork(pe.specTaskQueue.Pop().(ExecVersionView)) doWork(pe.specTaskQueue.Pop().(ExecVersionView))
} }
@ -597,12 +595,12 @@ func (pe *ParallelExecutor) Step(res *ExecResult) (result ParallelExecutionResul
type PropertyCheck func(*ParallelExecutor) error type PropertyCheck func(*ParallelExecutor) error
func executeParallelWithCheck(tasks []ExecTask, profile bool, check PropertyCheck, metadata bool, interruptCtx context.Context) (result ParallelExecutionResult, err error) { func executeParallelWithCheck(tasks []ExecTask, profile bool, check PropertyCheck, metadata bool, numProcs int, interruptCtx context.Context) (result ParallelExecutionResult, err error) {
if len(tasks) == 0 { if len(tasks) == 0 {
return ParallelExecutionResult{MakeTxnInputOutput(len(tasks)), nil, nil, nil}, nil return ParallelExecutionResult{MakeTxnInputOutput(len(tasks)), nil, nil, nil}, nil
} }
pe := NewParallelExecutor(tasks, profile, metadata) pe := NewParallelExecutor(tasks, profile, metadata, numProcs)
err = pe.Prepare() err = pe.Prepare()
if err != nil { if err != nil {
@ -636,6 +634,6 @@ func executeParallelWithCheck(tasks []ExecTask, profile bool, check PropertyChec
return return
} }
func ExecuteParallel(tasks []ExecTask, profile bool, metadata bool, interruptCtx context.Context) (result ParallelExecutionResult, err error) { func ExecuteParallel(tasks []ExecTask, profile bool, metadata bool, numProcs int, interruptCtx context.Context) (result ParallelExecutionResult, err error) {
return executeParallelWithCheck(tasks, profile, nil, metadata, interruptCtx) return executeParallelWithCheck(tasks, profile, nil, metadata, numProcs, interruptCtx)
} }

View file

@ -17,6 +17,8 @@ import (
type OpType int type OpType int
var numProcs = 8
const readType = 0 const readType = 0
const writeType = 1 const writeType = 1
const otherType = 2 const otherType = 2
@ -425,7 +427,7 @@ func runParallel(t *testing.T, tasks []ExecTask, validation PropertyCheck, metad
profile := false profile := false
start := time.Now() start := time.Now()
result, err := executeParallelWithCheck(tasks, false, validation, metadata, nil) result, err := executeParallelWithCheck(tasks, false, validation, metadata, numProcs, nil)
if result.Deps != nil && profile { if result.Deps != nil && profile {
result.Deps.Report(*result.Stats, func(str string) { fmt.Println(str) }) result.Deps.Report(*result.Stats, func(str string) { fmt.Println(str) })
@ -458,7 +460,7 @@ func runParallel(t *testing.T, tasks []ExecTask, validation PropertyCheck, metad
func runParallelGetMetadata(t *testing.T, tasks []ExecTask, validation PropertyCheck) map[int]map[int]bool { func runParallelGetMetadata(t *testing.T, tasks []ExecTask, validation PropertyCheck) map[int]map[int]bool {
t.Helper() t.Helper()
res, err := executeParallelWithCheck(tasks, true, validation, false, nil) res, err := executeParallelWithCheck(tasks, true, validation, false, numProcs, nil)
assert.NoError(t, err, "error occur during parallel execution") assert.NoError(t, err, "error occur during parallel execution")
@ -943,7 +945,7 @@ func TestBreakFromCircularDependency(t *testing.T) {
cancel() cancel()
// This should not hang // This should not hang
_, err := ExecuteParallel(tasks, false, true, ctx) _, err := ExecuteParallel(tasks, false, true, numProcs, ctx)
if err == nil { if err == nil {
t.Error("Expected cancel error") t.Error("Expected cancel error")
@ -976,7 +978,7 @@ func TestBreakFromPartialCircularDependency(t *testing.T) {
cancel() cancel()
// This should not hang // This should not hang
_, err := ExecuteParallel(tasks, false, true, ctx) _, err := ExecuteParallel(tasks, false, true, numProcs, ctx)
if err == nil { if err == nil {
t.Error("Expected cancel error") t.Error("Expected cancel error")

View file

@ -263,8 +263,6 @@ var parallelizabilityTimer = metrics.NewRegisteredTimer("block/parallelizability
// transactions failed to execute due to insufficient gas it will return an error. // transactions failed to execute due to insufficient gas it will return an error.
// nolint:gocognit // nolint:gocognit
func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error) { func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config, interruptCtx context.Context) (types.Receipts, []*types.Log, uint64, error) {
blockstm.SetProcs(cfg.ParallelSpeculativeProcesses)
var ( var (
receipts types.Receipts receipts types.Receipts
header = block.Header() header = block.Header()
@ -364,7 +362,7 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
backupStateDB := statedb.Copy() backupStateDB := statedb.Copy()
profile := false profile := false
result, err := blockstm.ExecuteParallel(tasks, profile, metadata, interruptCtx) result, err := blockstm.ExecuteParallel(tasks, profile, metadata, cfg.ParallelSpeculativeProcesses, interruptCtx)
if err == nil && profile && result.Deps != nil { if err == nil && profile && result.Deps != nil {
_, weight := result.Deps.LongestPath(*result.Stats) _, weight := result.Deps.LongestPath(*result.Stats)
@ -398,7 +396,7 @@ func (p *ParallelStateProcessor) Process(block *types.Block, statedb *state.Stat
t.totalUsedGas = usedGas t.totalUsedGas = usedGas
} }
_, err = blockstm.ExecuteParallel(tasks, false, metadata, interruptCtx) _, err = blockstm.ExecuteParallel(tasks, false, metadata, cfg.ParallelSpeculativeProcesses, interruptCtx)
break break
} }