Skip to content

Autoharness: verify harnesses in parallel by default - #4705

Merged
feliperodri merged 2 commits into
model-checking:mainfrom
tautschnig:autoharness-parallel
Aug 25, 2026
Merged

Autoharness: verify harnesses in parallel by default#4705
feliperodri merged 2 commits into
model-checking:mainfrom
tautschnig:autoharness-parallel

Conversation

@tautschnig

@tautschnig tautschnig commented Jul 31, 2026

Copy link
Copy Markdown
Member

Description

Autoharness typically generates hundreds of harnesses, for which the sequential default of the harness runner is a poor fit. In a top-100 crates.io evaluation (tracking issue #3832), 12 crates hit a 30-minute wall-clock cap; with -j 16 --output-format=terse, num-traits alone went from timing out at 1800s to completing all 1,983 generated harnesses in 330s.

Kani already has the machinery (--jobs, whose rayon pool covers the full per-harness pipeline: goto-instrument, loop-contract synthesis, CBMC). This PR makes autoharness use it by default.

To distinguish an explicit --output-format=regular from the clap default, output_format becomes Option<OutputFormat> with an accessor defaulting to Regular.

Defaulting rules (apply_autoharness_parallel_defaults), applied for the autoharness subcommand only:

  • --output-format is defaulted to terse when the user did not pass it.
  • --jobs is defaulted to -j (the thread pool's default thread count) when the user did not pass it and the resulting format is terse.

So:

invocation effective
autoharness -j + terse
autoharness --jobs=N -j N + terse
autoharness --output-format=terse -j + terse
autoharness --jobs=1 sequential + terse
autoharness --output-format=regular (or old) sequential, format as asked
autoharness --jobs=N --output-format=regular error (as before)
plain kani / cargo kani unchanged: sequential + regular; --jobs still requires --output-format=terse

The defaults are applied in main immediately after parsing and before validation, so validation sees the options the run will actually use. Otherwise a bare --jobs=N would be rejected for lacking --output-format=terse even though autoharness supplies exactly that default — i.e. the documented "pass -j <N> to control the number of threads" would not have worked. The call is idempotent and is repeated in setup_session so a session is configured correctly however it was constructed.

Note on memory: each thread runs its own CBMC process, so peak memory grows with the thread count, and the default is one thread per logical CPU. The docs call this out and point at --jobs=<N> as the knob.

Results themselves stay deterministic: harness_runner collects with rayon's indexed collect::<Result<Vec<_>>>(), which preserves input order, so the summary table, --sarif, and --export-json are unaffected by execution order. Only live stdout ordering varies, which is what the Thread N: prefix and the terse requirement address.

Testing

  • New cargo_autoharness_parallel test pins both halves of the default with order-independent assertions: terse output (absence of per-check detail, independent of the CPU count), thread prefixes (skipped in a single-CPU environment rather than failing spuriously), the sorted per-function summary lines, and the totals line.
  • New kani-driver unit tests: check_autoharness_parallel_defaults covers every combination in the table above plus "plain verification unaffected"; check_jobs_still_requires_terse pins that an explicit non-terse format with --jobs remains an error for both autoharness and plain verification.
  • Existing autoharness tests that assert on per-check detail (Status:/Description:/Check N:), which only --output-format=regular prints, now pass that flag — this also covers the opt-out path. That is 12 tests, including cargo_autoharness_bounded, cargo_autoharness_slices and cargo_autoharness_smart_pointers, which were added after this PR was opened.
  • The remaining autoharness tests (assume_invariant, fmt_impls, generics, list, raw_pointers, exclude_precedence) deliberately keep the new default, so CI exercises the parallel/terse path beyond the dedicated test. Their expected output only references the sorted summary/skipped tables and totals, and compiletest matches expected lines independently of order.
  • Full script-based-pre suite (66 tests), ui suite, cargo test -p kani-driver (81 tests), clippy --all-targets, and kani-fmt --check pass.
  • Manually verified every row of the table above, plus that plain kani/cargo kani output is unchanged.

Towards #3832 (evaluation throughput).

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.

@tautschnig
tautschnig requested a review from a team as a code owner July 31, 2026 18:44
Copilot AI review requested due to automatic review settings July 31, 2026 18:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added the Z-EndToEndBenchCI Tag a PR to run benchmark CI label Jul 31, 2026
@feliperodri feliperodri added the Z-Autoharness Issue related to autoharness subcommand label Jul 31, 2026
@feliperodri feliperodri added this to the Autoharness milestone Aug 18, 2026
Autoharness typically generates hundreds of harnesses, for which the
sequential default of the shared harness runner is a poor fit: in a top-100
crates.io evaluation, 12 crates hit a 30-minute wall-clock cap. Kani already
supports parallel harness verification behind --jobs (which requires
--output-format=terse); with -j 16, num-traits went from timing out at
1800s to completing all 1,983 generated harnesses in 330s.

Default autoharness to --jobs (thread-pool default) with terse output when
the user passes neither option. Explicit choices are always preserved:
--output-format=regular restores sequential verification with detailed
output, and the parse-time validation that --jobs requires terse output is
unaffected. Plain 'kani'/'cargo kani' verification is unchanged.

To distinguish an explicit --output-format=regular from the clap default,
the output_format argument becomes Option<OutputFormat> with an accessor
defaulting to Regular.

Existing autoharness tests assert on ordered per-harness output, so they
pin --output-format=regular (also covering the opt-out); a new test pins
the parallel default via order-independent assertions.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
@feliperodri
feliperodri force-pushed the autoharness-parallel branch from 1f63f3d to 324c598 Compare August 24, 2026 23:24
@feliperodri
feliperodri requested a review from a team as a code owner August 24, 2026 23:24
@feliperodri
feliperodri requested a balanced review from Copilot August 25, 2026 00:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 27 changed files in this pull request and generated 2 comments.

Comment thread tests/script-based-pre/cargo_autoharness_parallel/parallel.sh Outdated
Comment thread docs/src/reference/experimental/autoharness.md
@feliperodri
feliperodri force-pushed the autoharness-parallel branch from 324c598 to 4d57934 Compare August 25, 2026 00:15
@feliperodri

feliperodri commented Aug 25, 2026

Copy link
Copy Markdown
Member

Both addressed.

parallel.sh CPU detection: correct, and the guard was wrong in exactly the environment it was supposed to protect: getconf _NPROCESSORS_ONLN reports host-online CPUs, whereas rayon derives its default from available_parallelism (affinity/cgroup aware). Replaced the guard with RAYON_NUM_THREADS=2 on the invocation and made the thread-prefix assertion unconditional. This still discriminates the default rather than just pinning the pool size: rayon only consults RAYON_NUM_THREADS when the thread count is left unset, which is what the defaulted --jobs does — sequential verification passes an explicit num_threads(1) that overrides it. Verified both ways: with RAYON_NUM_THREADS=2 the default run emits Thread 0:/Thread 1:, while --output-format=regular emits no thread prefixes at all.

Docs example: also a real defect. The example now passes --output-format=regular, with a pointer to the Parallel verification section explaining why, so the command and the per-check Check/Status/Description output shown below it agree.

They assert on ordered per-harness regular output, like the autoharness
tests pinned previously; missed because their directory names do not match
the autoharness glob.

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
@feliperodri
feliperodri force-pushed the autoharness-parallel branch from 4d57934 to d342cec Compare August 25, 2026 00:37
@feliperodri
feliperodri enabled auto-merge August 25, 2026 00:46
@feliperodri
feliperodri added this pull request to the merge queue Aug 25, 2026
Merged via the queue into model-checking:main with commit 4e8976c Aug 25, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Z-Autoharness Issue related to autoharness subcommand Z-EndToEndBenchCI Tag a PR to run benchmark CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants