Skip to content

feat(attn): warn when an attention kernel compiles after warmup - #800

Merged
tdoublep merged 2 commits into
torch-spyre:mainfrom
tdoublep:worktree-late-compile-warning
Sep 8, 2026
Merged

feat(attn): warn when an attention kernel compiles after warmup#800
tdoublep merged 2 commits into
torch-spyre:mainfrom
tdoublep:worktree-late-compile-warning

Conversation

@tdoublep

@tdoublep tdoublep commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Description

Warmup records every attention variant the bucketer enumerates, but nothing tells you at
runtime when that coverage falls short. #799 removed the log line that fired on a lazy
compile, and it only ever covered the page kernel in any case — the batched decode kernel
is never recorded, so under SPYRE_BATCHED_DECODE it has no coverage guarantee at all.

Both serving dispatch sites now read Dynamo's graph counter across the call and warn once
if it moved. It is armed at the end of attention recording, so the paths that deliberately
skip recording (SPYRE_ATTN_RECORD=0, CompilationMode.NONE) keep compiling on first use
without complaint.

It should stay silent on the default path, where the recorder covers every variant
dispatch can reach — if it ever fires there, that is the bug it exists to report. Today a
missed variant is invisible except as an unexplained latency spike.

Test Plan

  • pytest tests/attention/test_spyre_attn_recorder.py tests/runtime/test_spyre_attn_bucketer.py -m "not upstream" — 63 passed, 5 skipped.
  • tests/attention/test_spyre_attn.py, subset covering both dispatch sites, on a card — 48 passed, 11 skipped.
  • bash format.sh — all hooks pass except ty, which reports the same diagnostics on this branch as on its base.

Checklist

  • I have read the contributing guidelines
  • My code follows the project's code style (run bash format.sh)
  • I have added tests for my changes (if applicable)
  • I have updated the documentation (if applicable)
  • My commits include a Signed-off-by: line (DCO compliance)

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

👋 Hi! Thank you for contributing.
Just a reminder: Make sure that your code passes all the linting checks, otherwise your PR won't be able to be merged. To do so, run ./format.sh.
Now you are good to go 🚀.

We also recommend installing prek and configuring it to check your code before every local commit.

@tdoublep
tdoublep force-pushed the worktree-late-compile-warning branch from 178ca41 to cc8d56e Compare September 8, 2026 09:53
The recorder claims warmup covers every variant a run can reach, but nothing
reported a miss at runtime: the log line that fired on a lazy compile is gone,
and it only ever covered the page kernel anyway. The batched decode kernel is
not recorded at all, so under SPYRE_BATCHED_DECODE it has no coverage guarantee.

Both dispatch sites now read Dynamo's graph counter across the call and warn
once if it moved. Armed at the end of attention recording, so the paths that
skip recording keep compiling on first use without warning.

Signed-off-by: Thomas Parnell <tpa@zurich.ibm.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tdoublep
tdoublep force-pushed the worktree-late-compile-warning branch from cc8d56e to 241eaa8 Compare September 8, 2026 09:57
@tdoublep
tdoublep marked this pull request as ready for review September 8, 2026 09:57
@tdoublep
tdoublep requested review from a team, bringlein and jvlunteren as code owners September 8, 2026 09:57

@jvlunteren jvlunteren left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The gap is genuine. #799 removed the log line that fired on lazy compiles, and, as the body says, it only ever covered the page kernel. The batched decode kernel is never recorded, so under SPYRE_BATCHED_DECODE there is no coverage guarantee at all.

The mechanism checks out. I verified counters["stats"]["unique_graphs"] increments once per graph compilation at output_graph.py:2933, inside compile_and_call_fx_graph, so a delta across the call detects a genuinely new graph, not a cache hit. That's the right counter, and it is the distinction #792 got wrong (a dict hit is not a recompile guarantee).

The attributability argument is the important claim, and it is stated in the docstring:

Dynamo's counter is process-wide but attributable across just this call: a compiled region runs no eager ops, and torch-spyre compiles every eager aten op.

I would accept that, with one reservation: it holds only while nothing else on another thread compiles concurrently. In a single-tenant serving process that is fine, and a false positive here costs one spurious warning, not a failure. Worth noting rather than blocking.

The counter delta is attributable to a single call only while nothing else
compiles on another thread. Holds for a single-tenant serving process, and a
violation costs a spurious warning rather than a wrong result.

Signed-off-by: Thomas Parnell <tpa@zurich.ibm.com>
@tdoublep
tdoublep enabled auto-merge September 8, 2026 11:34
@github-actions github-actions Bot added the ready label Sep 8, 2026
@spyre-ci

spyre-ci Bot commented Sep 8, 2026

Copy link
Copy Markdown

⚙️ Auto-merge enabled — dispatched /spyre-test for 51151e29 because the required Spyre Test gate had not run on this commit.

Automated: enabling auto-merge requests the gate, so the PR is not left waiting on a check nobody asked for. Re-enabling auto-merge on this same commit will not dispatch again.

@spyre-ci

spyre-ci Bot commented Sep 8, 2026

Copy link
Copy Markdown

🔄 spyre-test: running

Triggered by: /spyre-test comment

Plan (build waves + dependencies, per arch)

amd64

flowchart LR
  subgraph Lamd64_0["amd64 L0 · 1 parallel"]
    n_amd64_torch_spyre_torch_spyre_dev["torch-spyre/torch-spyre-dev 🟢 ↺<br/>image · 72411d836e22"]
  end
  subgraph Lamd64_1["amd64 L1 · 1 parallel"]
    n_amd64_hf_adapters_hf_adapters_dev["hf-adapters/hf-adapters-dev 🟢<br/>image · 5247b8e41856"]
  end
  subgraph Lamd64_2["amd64 L2 · 1 parallel"]
    n_amd64_spyre_inference_spyre_inference_dev["spyre-inference/spyre-inference-dev 🟢<br/>image · 8f1976c5cef2"]
  end
  n_amd64_torch_spyre_torch_spyre_dev --> n_amd64_hf_adapters_hf_adapters_dev
  n_amd64_hf_adapters_hf_adapters_dev --> n_amd64_spyre_inference_spyre_inference_dev
  classDef sPending fill:#eceff1,stroke:#90a4ae,color:#37474f
  classDef sBuilding fill:#fff8e1,stroke:#f9a825,color:#5d4037,stroke-width:2px
  classDef sOk fill:#e8f5e9,stroke:#43a047,color:#1b5e20
  classDef sReused fill:#e3f2fd,stroke:#1e88e5,color:#0d47a1
  classDef sFailed fill:#ffebee,stroke:#e53935,color:#b71c1c,stroke-width:2px
  classDef sDropped fill:#f5f5f5,stroke:#bdbdbd,color:#9e9e9e
  class n_amd64_torch_spyre_torch_spyre_dev sReused;
  class n_amd64_hf_adapters_hf_adapters_dev sOk;
  class n_amd64_spyre_inference_spyre_inference_dev sOk;
Loading

🔨 orch trigger-pr-validation · arches amd64 · fp amd64=dc820e01

level component arch build smoke unit integration trunk regression perf
L0 torch-spyre/torch-spyre-dev amd64 ✅ ok (reused) 🟢 · 🟢 gha · · ·
L1 hf-adapters/hf-adapters-dev amd64 ✅ ok 🟢 · 🟢 gha · · ·
L2 spyre-inference/spyre-inference-dev amd64 ✅ ok 🟢 · 🧪 · · ·

@tdoublep
tdoublep added this pull request to the merge queue Sep 8, 2026
Merged via the queue into torch-spyre:main with commit 83efad2 Sep 8, 2026
50 of 51 checks passed
@spyre-ci

spyre-ci Bot commented Sep 8, 2026

Copy link
Copy Markdown

🔄 merge-queue-integration: running

Plan (build waves + dependencies, per arch)

amd64

flowchart LR
  subgraph Lamd64_0["amd64 L0 · 1 parallel"]
    n_amd64_hf_adapters_hf_adapters_dev["hf-adapters/hf-adapters-dev 🔴<br/>image · bc0ac51daa38"]
  end
  subgraph Lamd64_1["amd64 L1 · 1 parallel"]
    n_amd64_spyre_inference_spyre_inference_dev["spyre-inference/spyre-inference-dev 🔴<br/>image · 29d17914b03c"]
  end
  n_amd64_hf_adapters_hf_adapters_dev --> n_amd64_spyre_inference_spyre_inference_dev
  classDef sPending fill:#eceff1,stroke:#90a4ae,color:#37474f
  classDef sBuilding fill:#fff8e1,stroke:#f9a825,color:#5d4037,stroke-width:2px
  classDef sOk fill:#e8f5e9,stroke:#43a047,color:#1b5e20
  classDef sReused fill:#e3f2fd,stroke:#1e88e5,color:#0d47a1
  classDef sFailed fill:#ffebee,stroke:#e53935,color:#b71c1c,stroke-width:2px
  classDef sDropped fill:#f5f5f5,stroke:#bdbdbd,color:#9e9e9e
  class n_amd64_hf_adapters_hf_adapters_dev sOk;
  class n_amd64_spyre_inference_spyre_inference_dev sOk;
Loading

🔨 orch trigger-pr-validation · arches amd64 · fp amd64=7763559f

level component arch build smoke unit integration trunk regression perf
L0 hf-adapters/hf-adapters-dev amd64 ✅ ok 🟢 · 🟡 gha · · ·
L1 spyre-inference/spyre-inference-dev amd64 ✅ ok 🟢 · · · 🟡 gha ·

@spyre-ci

spyre-ci Bot commented Sep 8, 2026

Copy link
Copy Markdown

GHA test runs:

@spyre-ci

spyre-ci Bot commented Sep 8, 2026

Copy link
Copy Markdown

GHA test runs:

@spyre-ci

spyre-ci Bot commented Sep 8, 2026

Copy link
Copy Markdown

GHA test runs:

@spyre-ci

spyre-ci Bot commented Sep 8, 2026

Copy link
Copy Markdown

GHA test runs:

@spyre-ci

spyre-ci Bot commented Sep 8, 2026

Copy link
Copy Markdown

GHA test runs:

tdoublep added a commit to tdoublep/spyre-inference that referenced this pull request Sep 8, 2026
_record_one dispatched to self._attn_fn unconditionally, so with
SPYRE_LX_KV_LAYOUT=1 warmup fed the (page, kv_head)-folded cache into the
slot-major kernel and every variant died in
k_page.squeeze(0).permute(1, 0, 2): Recorded 0/12 for all 40 layers. Only
forward() had learned about _lx_attn_fn. The variants then compiled in the
serving path, costing a 325s warmup iteration against ~17s once recorded.

The recorder now builds the folded kernel's own arguments -- per-block
[num_kv_heads, 1] index tables (one real tensor per block, torch-spyre#3770),
head index tables, block_size, per-group ALiBi tiles, fused-store row tables --
and calls it inside _capped_attn_cores, so the recorded graph is the one
dispatch reuses rather than an uncapped near-miss.

Also routes the LX dispatch site through torch-spyre#800's _call_kernel, which that PR
predates, so the folded path is covered by the late-compile warning too.

Measured on granite-3.3-8b, 2048 in / 64 out / bs 1, torch-spyre built from
#4153: 14.333s on main vs 13.829s with SPYRE_LX_KV_LAYOUT=1, i.e. -3.5%
latency, distributions non-overlapping, both legs reporting no late compiles.

Signed-off-by: Thomas Parnell <tpa@zurich.ibm.com>
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.

2 participants