Skip to content

feat(gemma4): generate the hd512 global prefill kernel and serve it behind a knob - #1052

Merged
FeathBow merged 1 commit into
pegainfer-project:mainfrom
FeathBow:feat/gemma4-hd512-prefill
Sep 17, 2026
Merged

FeathBow merged 1 commit into
pegainfer-project:mainfrom
FeathBow:feat/gemma4-hd512-prefill

Conversation

@FeathBow

Copy link
Copy Markdown
Collaborator

Description

Closes #1051

The shape was never the problem; the budget was. FlashInfer's FA2 path serves head_dim 512 by time-sharing K and V in one shared-memory buffer with a 64-key tile, a layout its own comment ties to the 99 KB SKUs. On a GH200 that leaves the kernel at 19–28% of the measured roofline where FlashAttention 4 holds 44–49%, and at 31B the ten global layers' prefill is the largest account of a long prompt.

A generated kernel, built where the line lives. pegainfer-gemma4/kernels/ carries a TileLang definition of the varlen paged prefill at head_dim 512 and a generator that emits the CUDA, a launcher table and a host stub at build time; the kernels crate's AOT section now takes a family table, so this family sits beside the routed lane's rather than duplicating its build logic. A build without TileLang carries a stub that refuses to serve, and says so.

The global pool is paged at the key block. The global family's page size moves to 64 so one key tile is one TMA copy; the sliding pool is untouched in this change.

One knob, three lines it can fall on. PEGAINFER_GLOBAL_ATTN=tilelang routes the global family's prompt rows through the generated kernel; unset or off keeps the incumbent and byte-identical serving. The walk inside the kernel is bounded by the CTA count the host computed for the plan, so a CTA past the plan does nothing rather than reading past it.

Measured, not assumed. A paired harness (hd512_prefill_bench) times both kernels on one pool and one plan; the serving oracle holds the generated kernel against the incumbent on the checkpoint.

Test Env

  • One GH200, CUDA 12.6 toolchain for the kernels crate, TileLang 0.1.12 for the generated family; Gemma 4 31B bf16 for the serving A/B and the 31B oracles, the pinned 12B checkpoint and fixtures for the maintained gate suite.

Verification

Bound to the tip of this range; every run on the box above.

  • AOT gate: every prefill case bit-identical between the AOT and JIT builds, |Δ| ≤ 0.002 against an fp32 reference, hostile padding past the plan left unwritten, the refusal rows all refused.
  • Paired kernel harness at the 31B global shape: 32K chunk 78.1 → 32.6 ms (2.40×), 163K chunk 423.6 → 180.8 ms (2.34×).
  • Serving oracle on the checkpoint: the generated prefill within |Δ logit| 0.5 of the incumbent at 12B and 0.625 at 31B against a line of 2.0, the incumbent bit-stable run to run; the maintained gate suite green on the 12B fixtures with the split state's oracle numbers unchanged.
  • Same-binary A/B on 31B, the knob the only difference, single request, four prompt lengths, four rounds, 12 kept requests per cell, round spread ≤ 0.7%, every paired interval narrower than a point and off zero: TTFT −9.2 .. −8.5% at 10,602 tokens, −20.4 .. −19.8% at 40,002, −28.6 .. −27.8% at 81,653, −36.7 .. −36.6% at 163,336; E2EL −2.1 .. −2.0% to −33.2 .. −33.0% over the same cells. TPOT −0.3 .. +0.1% (decode does not touch this kernel) and peak device memory identical at 82,655 MiB on both arms, which is what a prefill-only change should look like. Against the 12B-era prediction from the kernel ratios the saving is 26.3 s of a predicted 29.0 s at 163K; the remainder sits in the short-kv chunks where the old kernel was least behind.
  • Three-arm fair A/B against vLLM on the same card (vLLM's own bench client, both engines at the 165,888 ceiling vLLM negotiated, 8192-token chunk from vLLM's log, KV bf16 on both sides, prefix caching off): the generated kernel moves the TTFT crossover to about 40K tokens and the E2EL crossover to about 81.6K; at 163,336 TTFT −12.0 .. −11.7% and E2EL −7.9 .. −7.6% against vLLM, with TPOT untouched and still behind (+26.8 .. +27.3%), which the follow-up changes take up.
  • cargo fmt --check, clippy -D warnings on the kernels and gemma4 crates including tests and examples, lib tests green.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0d71c9a3cf

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +75 to +76
HEADS = 32
GROUPS = 8

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Gate the kernel to the generated attention geometry

When PEGAINFER_GLOBAL_ATTN=tilelang is used with the supported 12B checkpoint, the engine accepts the knob because startup checks only whether any generated body exists, but this kernel is compiled exclusively for 32 query heads and 4 global KV heads; the published 12B geometry is 16/1, so the launcher's head-count guard returns cudaErrorInvalidValue on the first global prefill and the request fails. Generate and dispatch every supported geometry, or reject incompatible checkpoints during engine loading rather than accepting a configuration that cannot serve a prompt.

Useful? React with 👍 / 👎.

Comment on lines +1692 to 1695
fn tilelang_arch(sm_targets: &[String]) -> Option<String> {
let max_sm = sm_targets
.iter()
.filter_map(|sm| sm_numeric_prefix(sm))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve every requested architecture in multi-SM builds

When PEGAINFER_CUDA_SM lists multiple targets, or the build host exposes mixed GPU generations, selecting only the maximum SM causes tilelang_gencode to emit SASS solely for that architecture. The crate-level gemma4_tilelang cfg still reports the kernel as built, so selecting one of the lower devices with PEGAINFER_GLOBAL_ATTN=tilelang passes startup validation and later fails at the function attribute or launch with no compatible kernel image. Generate an image for each supported requested SM or validate the selected runtime device before accepting the knob.

AGENTS.md reference: AGENTS.md:L39-L39

Useful? React with 👍 / 👎.

Comment thread pegainfer-gemma4/kernels/generate.py Outdated
Comment on lines +622 to +624
lines = [f"CU_PATH={cu_path}"]
lines.append(f"TILELANG_TEMPLATE_PATH={template_include}")
lines.append(f"CUTLASS_INCLUDE_DIR={cutlass_include}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make pre-generated manifests relocatable

When --vendor-includes output is copied to a TileLang-free build host—the purpose of the pre-generated tier—these manifest entries retain the producer's absolute paths, or paths relative to the producer's working directory. parse_tilelang_manifest resolves them directly rather than relative to manifest.txt, so moving the supposedly self-contained directory makes its CUDA file and vendored include roots appear missing and aborts the build. Store paths relative to the artifact directory and resolve them against the configured pre-generated directory.

Useful? React with 👍 / 👎.

@FeathBow
FeathBow force-pushed the feat/gemma4-hd512-prefill branch 3 times, most recently from b6b1554 to 51d103d Compare September 16, 2026 23:15
@FeathBow

Copy link
Copy Markdown
Collaborator Author

@codex review again plx

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 51d103d4c8

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

for line in text.lines() {
if let Some(value) = line.strip_prefix("CU_PATH=") {
cu_files.push(PathBuf::from(value.trim()));
cu_files.push(base.join(value.trim()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid double-prefixing K3 pre-generated paths

When using the documented K3 pre-generated flow (generate.py --out-dir pregen --vendor-includes followed by PEGAINFER_K3_TILELANG_PREGEN=pregen), the unchanged K3 generator writes entries such as CU_PATH=pregen/... and TILELANG_TEMPLATE_PATH=pregen/include/...; unconditionally joining those entries to the manifest directory turns them into pregen/pregen/..., so parse_tilelang_manifest reports missing CUDA files and the offline K3 build fails. Make K3 emit manifest-relative paths as the Gemma generator now does, or retain compatibility with its producer-relative entries.

AGENTS.md reference: AGENTS.md:L163-L163

Useful? React with 👍 / 👎.

Comment on lines +1936 to +1938
for knob in ["PYTHON", "PREGEN", "JOBS"] {
println!("cargo:rerun-if-env-changed=PEGAINFER_{label}_TILELANG_{knob}");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Track the shared TileLang interpreter variable

On an incremental Gemma4 or K3 build that previously selected the stub tier, setting PEGAINFER_TILELANG_PYTHON does not cause Cargo to rerun this build script because only the family-specific PEGAINFER_<label>_TILELANG_PYTHON variables are registered here, even though find_tilelang_python also reads the shared variable. The stale stub therefore remains and PEGAINFER_GLOBAL_ATTN=tilelang continues to be refused until the user cleans the build or changes a tracked input; add a rerun-if-env-changed entry for the shared selector.

Useful? React with 👍 / 👎.

@FeathBow
FeathBow force-pushed the feat/gemma4-hd512-prefill branch 2 times, most recently from b179c61 to a6d2e51 Compare September 16, 2026 23:26
@FeathBow

Copy link
Copy Markdown
Collaborator Author

@codex review again

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Some(if max_sm >= 90 {
format!("sm_{max_sm}a")
} else {
format!("sm_{max_sm}")

P1 Badge Skip TMA generation on pre-Hopper targets

When Gemma 4 is built for a supported pre-Hopper device such as SM89 and the discovered Python happens to have TileLang installed, this returns sm_89 and generate_tilelang_artifacts invokes the Gemma generator instead of selecting the stub tier. That generator requires TMA descriptors and explicitly errors when lowering produces none, while TMA is unavailable before SM90, so merely installing TileLang makes the otherwise-supported Gemma build fail. Return None for Gemma targets below SM90 or fall back to the stub when this TMA-only generation is inapplicable.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread pegainfer-gemma4/src/serve_oracle.rs Outdated
Comment on lines +605 to +609
{
let refusal = crate::engine::tilelang_geometry_refusal(&config)
.expect_err("a checkpoint the kernel was not compiled for must be refused");
eprintln!("geometry {geometry:?} is not this checkpoint's: {refusal}");
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Exercise the generated geometry in the production gate

When the maintained gate suite is run with its usual 12B checkpoint (16 query heads over 1 global KV head), the generated 32/4 geometry mismatches and this branch returns success after testing only startup refusal. Consequently the test named as the production-path comparison never executes the replacement kernel, leaving the output-changing path with only synthetic kernel coverage; run this gate with a compatible checkpoint or fail/skip it explicitly rather than counting the refusal as the model-level parity evidence.

AGENTS.md reference: AGENTS.md:L122-L124

Useful? React with 👍 / 👎.

@FeathBow
FeathBow force-pushed the feat/gemma4-hd512-prefill branch from a6d2e51 to c9bac8c Compare September 17, 2026 07:50
@FeathBow

Copy link
Copy Markdown
Collaborator Author

@codex review

@FeathBow
FeathBow force-pushed the feat/gemma4-hd512-prefill branch from c9bac8c to 98883f2 Compare September 17, 2026 08:27
…ehind a knob

Signed-off-by: Feathbow <feathbow@gmail.com>
@FeathBow
FeathBow force-pushed the feat/gemma4-hd512-prefill branch from 98883f2 to 8a5d45c Compare September 17, 2026 09:00
@FeathBow
FeathBow merged commit b112436 into pegainfer-project:main Sep 17, 2026
17 checks passed
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.

gemma4: the global hd512 prefill runs at a fifth of the Hopper roofline

1 participant