Problem
The Foundry baseline in scfuzzbench is not saturating CPU during invariant campaigns even when we pass --threads.
Concrete example:
PR #9 adds extra invariant_noop_* functions to the target to keep the machine busier, but that is only inflating invariant-check overhead. It is not the real fix.
Diagnosis
This is not a scfuzzbench runner misconfiguration.
scfuzzbench already passes --threads to Foundry in fuzzers/foundry/run.sh when FOUNDRY_THREADS is set.
The real bottleneck is in the invariant execution model inherited from alpharush:
- suite-level parallelism is across contracts, but scfuzzbench runs
forge test --mc CryticToFoundry, so there is only one suite to execute.
crates/forge/src/runner.rs keeps only the first invariant function as the top-level test entrypoint with the comment: Only keep the first invariant function — the campaign checks all invariant_fns.
crates/evm/evm/src/executors/invariant/mod.rs runs a single sequential while continue_campaign(runs) campaign.
- the same invariant executor creates exactly one worker corpus with
WorkerCorpus::new(0, ...).
- by contrast, the ordinary fuzz executor in
crates/evm/evm/src/executors/fuzz/mod.rs already has a real multi-worker model using rayon and num_workers.
So the current behavior is:
--threads helps when there are multiple contracts/suites.
- for one large invariant suite, CPU utilization stays low because the invariant campaign itself is effectively single-worker.
- adding noop invariants does not create principled parallelism; it just adds more checks inside the same campaign.
Desired fix
Fork alpharush into aviggiano/foundry and implement real multi-worker parallelism for invariant campaigns.
Concretely:
- Reuse the fuzz executor's worker model as the design reference.
- Allow a single invariant campaign to fan out across multiple workers when
--threads > 1.
- Preserve existing failure persistence, shrinking, and replay semantics.
- Keep determinism expectations reasonable for seeded runs.
- Add a regression/benchmark test that shows one-contract invariant suites can scale with threads instead of requiring target-side noop padding.
Non-goals
- Do not rely on target-side hacks like extra noop invariants.
- Do not push this burden into scfuzzbench if the real limitation is inside Foundry's invariant executor.
Acceptance signal
On a single-contract benchmark like CryticToFoundry, increasing --threads should materially increase CPU utilization and throughput during invariant campaigns, without needing PR 9 style target inflation.
Problem
The Foundry baseline in scfuzzbench is not saturating CPU during invariant campaigns even when we pass
--threads.Concrete example:
PR #9 adds extra
invariant_noop_*functions to the target to keep the machine busier, but that is only inflating invariant-check overhead. It is not the real fix.Diagnosis
This is not a scfuzzbench runner misconfiguration.
scfuzzbenchalready passes--threadsto Foundry infuzzers/foundry/run.shwhenFOUNDRY_THREADSis set.The real bottleneck is in the invariant execution model inherited from alpharush:
forge test --mc CryticToFoundry, so there is only one suite to execute.crates/forge/src/runner.rskeeps only the first invariant function as the top-level test entrypoint with the comment:Only keep the first invariant function — the campaign checks all invariant_fns.crates/evm/evm/src/executors/invariant/mod.rsruns a single sequentialwhile continue_campaign(runs)campaign.WorkerCorpus::new(0, ...).crates/evm/evm/src/executors/fuzz/mod.rsalready has a real multi-worker model usingrayonandnum_workers.So the current behavior is:
--threadshelps when there are multiple contracts/suites.Desired fix
Fork alpharush into
aviggiano/foundryand implement real multi-worker parallelism for invariant campaigns.Concretely:
--threads> 1.Non-goals
Acceptance signal
On a single-contract benchmark like
CryticToFoundry, increasing--threadsshould materially increase CPU utilization and throughput during invariant campaigns, without needing PR 9 style target inflation.