Skip to content

Performance notes

Parallel resolver execution

Commit: parallel resolver execution via bounded goroutine pool.

Design

  • Threshold: len(rules) >= 8 && runtime.NumCPU() > 1 → parallel
  • Concurrency: bounded by runtime.NumCPU() via semaphore channel
  • Determinism: results collected into per-rule slots, merged in rule-ID order
  • Panic recovery: per-goroutine, same as serial path

Benchmark results (Apple M1 Max, 10 cores)

BenchmarkEvaluate_Serial-10        1000   ~1.7 µs/op   (4 trivial rules)
BenchmarkEvaluate_Parallel-10      1000  ~32.0 µs/op   (16 trivial rules)

Interpretation: For trivial resolvers (no-op functions), goroutine overhead dominates and serial is ~20x faster. The parallel path benefits repos with:

  • 8+ rules
  • Resolvers that do real file scanning (ms-scale work)
  • Large repos (50k+ files where each resolver walks the file list)

For archfit's own scan (17 rules, ~200 files), the total evaluation time is <5ms either way — dominated by collector I/O, not resolver execution.

Race safety

TestEvaluate_ParallelDeterminism runs 100 iterations with -race and asserts byte-identical JSON output. This is enforced in CI.

When to revisit

The threshold (8 rules) and concurrency limit (NumCPU()) are conservative. If archfit grows to 50+ rules or resolvers begin doing heavier analysis (AST parsing, content scanning), lower the threshold or increase the pool.