Skip to content

feat: compiler-phase timing flamegraphs from tracing spans - #13300

Draft
TomAFrench wants to merge 3 commits into
masterfrom
tf/compiler-span-flamegraph
Draft

feat: compiler-phase timing flamegraphs from tracing spans#13300
TomAFrench wants to merge 3 commits into
masterfrom
tf/compiler-span-flamegraph

Conversation

@TomAFrench

@TomAFrench TomAFrench commented Jul 7, 2026

Copy link
Copy Markdown
Member

Description

Problem

We have no easy way to see where nargo compile spends its time. The compiler already emits tracing spans as JSON logs (via NARGO_LOG_DIR + NOIR_LOG), but nothing consumes them, and some key phases (individual SSA passes, parsing) had no spans at all.

Summary

Adds a compiler-phase profiling workflow:

just profile-compiler test_programs/benchmarks/semaphore_depth_10

produces, from the span logs of a single nargo compile run:

  • compiler-flamegraph.svg — an aggregated, self-time-weighted flamegraph of compiler phases (inferno).
  • compiler-trace.json — a per-invocation timeline that opens in Perfetto.

Changes:

  • Instrumentation: a ssa_pass{pass=...} span around every SSA pass in SsaBuilder::try_run_pass, and a span on parse_program (lexing is lazily interleaved with parsing, so there is a single combined span).
  • Post-processor: a new internal dev tool, tooling/compiler_profiler (noir-compiler-profiler), that streams the JSON span logs, reconstructs the call tree from close events (which carry the authoritative ancestor chain plus time.busy/time.idle), and emits both artifacts. It is deliberately not part of the user-facing noir-profiler, which profiles Noir programs rather than the compiler.
  • Untracked time is explicit: un-instrumented time inside a phase shows as a (self) frame, wall-clock time outside any root span as (untracked), and the tool reports what fraction of wall clock the flamegraph accounts for, warning on anomalies (unclosed spans, children exceeding their parent).
  • Cross-thread spans (parallel workspace parsing, compile_program itself) appear as detached roots in tracing; they are re-parented by time containment.
  • just profile-compiler recipe + tooling/compiler_profiler/README.md documenting usage, span filters, and how to read the output.

Per-pass durations in the flamegraph match nargo compile --benchmark-codegen to the millisecond across all 74 pipeline steps.

The default span filter (trace,noirc_frontend::elaborator=info) keeps logs small and timings accurate; full NOIR_LOG=trace additionally captures the elaborator's per-expression spans for a deep frontend breakdown at the cost of multi-GB logs and significant observer distortion.

Additional Context

The flamegraph for semaphore_depth_10 (~2.2s cold compile) breaks down as: ~0.4s parallel parsing, ~0.6s check/elaboration, ~1.1s SSA passes + ACIR gen, ~40ms monomorphization.

Documentation

Check one:

  • No documentation needed.
  • Documentation included in this PR.
  • [For Experimental Features] Documentation to be submitted in a separate PR.

PR Checklist

  • I have tested the changes locally.
  • I have formatted the changes with Prettier and/or cargo fmt on default settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant