Skip to content

Parallel solving#1046

Merged
MikaelMayer merged 359 commits into
mainfrom
issue-1045-feature-request-parallel-solving
May 20, 2026
Merged

Parallel solving#1046
MikaelMayer merged 359 commits into
mainfrom
issue-1045-feature-request-parallel-solving

Conversation

@MikaelMayer
Copy link
Copy Markdown
Contributor

@MikaelMayer MikaelMayer commented Apr 24, 2026

Fixes #1045

Summary

Adds a --parallel N flag that runs up to N solver instances concurrently when verifying proof obligations. Without the flag (or with --parallel 1), behavior is unchanged (sequential).

Problem

Verification of programs with many obligations is bottlenecked by sequential solver invocations. Each obligation spawns a separate solver process, waits for the result, then moves to the next.

Solution

When --parallel N is specified (N > 1), the verification pipeline splits into two phases:

  1. Sequential preprocessing (fast): determine checks, preprocess obligations, encode to SMT terms. Obligations resolved by the evaluator are handled immediately.
  2. Parallel solver dispatch (slow): obligations that need the solver are placed in a shared queue. N worker tasks (on dedicated threads) continuously pull from the queue — when a solver finishes, it immediately picks up the next unsolved obligation. Results are collected in original obligation order so output is deterministic.

The worker pool design avoids the "wait for slowest in batch" bottleneck: if one obligation takes 10s and others take 1s, the fast-finishing workers immediately start on the next obligation instead of idling.

stopOnFirstError is supported via a shared flag: on failure, workers stop claiming new jobs. Already-running jobs complete naturally; skipped jobs leave their placeholder results in place.

Both the incremental and batch solver paths are safe for parallel use: the incremental backend spawns independent solver processes, and the batch path uses atomic modifyGet for filename counter generation.

Pluggable discharge function: The full public API (Strata.verify, Core.verify, verifySingleEnv, mkDefaultCoreSMTSolver) accepts a mkDischarge : MkDischargeFn parameter (defaulting to mkDischargeFn). External solvers (e.g. using the AbstractSolver API) can provide their own discharge function factory.

Performance

Benchmark: 30 obligations across 2 programs, z3 4.12.2, avg over 3 runs:

Mode Time Speedup
--parallel 1 (sequential) 921ms baseline
--parallel 2 491ms 1.88x
--parallel 3 405ms 2.27x
--parallel 4 356ms 2.59x

Sweet spot is --parallel 3-4. The 1→2 jump gives the biggest single improvement (1.88x), then diminishing returns as solver spawn overhead and sequential preprocessing become the bottleneck.

Testing

All tests that pass without --parallel also pass with it. The sequential path (--parallel 1, the default) is unchanged.

MikaelMayer and others added 30 commits April 20, 2026 20:00
Merge main (3 new commits including mergeByAssertion PR #981).
Fix VCGPathTests.getEvalStats to use ObligationExtraction instead of
E.deferred.foldl since typeCheckAndEval now returns Program.
- Fix duplicate label clash warning by running symbolicEval separately
  (not in pipeline loop) to avoid re-running Program.eval for SMT env
- Use phase name in error messages (e.g. '❌ TypeCheck Error.' instead
  of '❌ Transform Error.')
- symbolicEval returns Env alongside Program for SMT encoding
- Filed #986 for DDM translator type alias issue
- Remove extra warning from DuplicateAssumeLabels test
The typeCheck pipeline phase now formats errors with file range and
'❌ Type checking error.' prefix, matching the old format. The pipeline
loop passes errors through without adding its own prefix.

Update .expect files and test expected outputs for the new format.
- symbolicEval and ANF are back in the pipeline loop (all phases unified)
- SMT env uses buildEvalEnv + Program.eval (label clash warning remains
  as a known issue until SMT env construction is fully decoupled)
- Closed #986 (was filed against branch, not main)
- Reverted symbolicEval to return (Program, Statistics) without Env
Local function declarations (funcDecl in procedure bodies) require
captureFreevars from the evaluation context. buildSMTEnv can't
replicate this without running Program.eval. Reverted to using
Program.eval for SMT env construction.

The duplicate label clash warning remains as a known limitation.
Added collectFuncDecls helper to buildSMTEnv for future use.
…rogram.eval

The obligations program now includes function declarations from the
evaluation environment (post-eval factory minus initial factory) and
distinct constraints. This allows buildSMTEnv to construct the SMT
encoding context from the program itself, eliminating the second
Program.eval call that caused the duplicate label clash warning.

Performance (Bubble.bpl, 34 obligations):
- Main: Pipeline 0ms, VC discharge 731ms, Total ~809ms
- Branch: Pipeline 77ms, VC discharge 820ms, Total ~953ms
- Overhead: ~18% (77ms pipeline phases + ANF encoding impact)
… prefix

- Rename transformPipelinePhases/corePipelinePhases so corePipelinePhases
  includes all phases (transforms + typeCheck + symbolicEval + ANF)
- Remove byte range from typeCheck error format (was regression from
  using formatRange without fileMap)
- Restore '❌ Type checking error.' prefix for symbolicEval errors
- Revert .expected files to main's format
- Update test expected outputs
…dbg_trace

- Run typeCheck/symbolicEval outside the pipeline loop so DiagnosticModel
  errors with source locations propagate directly instead of being
  converted to strings (which lost the fileRange).
- Remove leftover dbg_trace in verifySingleEnv.
- Restore source locations in expected test outputs.
…errors, remove Core-level ANF

- Change pipeline error type (Err) from String to DiagnosticModel so that
  source locations propagate through the pipeline without conversion.
- Move type checking and symbolic evaluation into corePipelinePhases as
  proper PipelinePhase entries, running inside the pipeline loop.
- Remove Core-level ANF encoder from the pipeline to eliminate extra
  variable declarations; the SMT-level ANF already handles deduplication.
- Update all throw sites in transform passes to use DiagnosticModel.
- Restore all debug traces to match main.
Remove the SMT-level ANF (defineTerm creating define-fun entries and
terms cache) from Encoder.lean. The SMT encoder now passes terms through
directly, making it a simple 1-1 translation.

Wire anfEncoderPipelinePhase into corePipelinePhases after symbolic
evaluation, so common subexpression deduplication happens at the Core
level before SMT encoding.

Update test expectations: SMT output no longer contains define-fun
$__t.N entries; assertions contain inline terms instead.
Replace O(n² × tree_size) subsumption check with hash-based O(n) lookup.
Replace per-target sequential body traversal with single-pass batch
replacement using a HashMap.

HeapReasoning ANF phase: 122ms → 10ms
Bubble ANF phase: 55ms → 5ms
…igation

The oblProgram from symbolicEval inlines axioms as assume statements but
does not include axiom declarations. This caused preprocessObligation to
have an empty axiomNames list, preventing .Precise mode from identifying
and excluding axiom assumptions. The fix passes axiom names and the
original program (with axiom declarations) from the verify function.
… metadata for errors

- Move isLeaf, hasBVar to LExpr.lean; getExprType? to Expressions.lean
- Move mapExprs, collectExprs to Statement.lean
- Replace exprSize with LExpr.size
- Extract anfVarPrefix constant shared between ANFEncoder and Verifier
- Revise ANFEncoder docstring to be concise
- Fix non-stateless comment in Core.lean
- Use metadata source locations in PrecondElim error messages
- Add focused symbolic eval tests (detIf, nondetIf, blockExit)
…ould-b

# Conflicts:
#	StrataTest/Languages/Core/Tests/ProgramTypeTests.lean
…_id theorem, fix toSMTTermString tests, fix DDM printer formatting
…-term

# Conflicts:
#	Strata/Languages/Core/Options.lean
#	StrataMain.lean
@MikaelMayer MikaelMayer requested a review from aqjune-aws May 19, 2026 17:22
Comment thread Strata/Languages/Core/Verifier.lean Outdated
@MikaelMayer MikaelMayer enabled auto-merge May 19, 2026 20:30
aqjune-aws
aqjune-aws previously approved these changes May 19, 2026
…quest-parallel-solving

Resolve conflicts: integrate main's PipelineContext profiling with
parallel dispatch. Re-add SolverJob, dispatchSolverJob,
dispatchJobsParallel, and parallel/sequential branch in verifySingleEnv.
Re-add --parallel CLI flag in StrataMainLib.
@MikaelMayer MikaelMayer changed the title Feature request: Parallel solving Parallel solving May 20, 2026
@MikaelMayer MikaelMayer added this pull request to the merge queue May 20, 2026
Merged via the queue into main with commit 7b72423 May 20, 2026
19 checks passed
@MikaelMayer MikaelMayer deleted the issue-1045-feature-request-parallel-solving branch May 20, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Parallel solving

3 participants