Skip to content

feat(kernels): batched decode prep entries at both head dims - #892

Merged
FeathBow merged 1 commit into
pegainfer-project:mainfrom
FeathBow:feat/gemma4-batched-decode-prep
Aug 16, 2026
Merged

feat(kernels): batched decode prep entries at both head dims#892
FeathBow merged 1 commit into
pegainfer-project:mainfrom
FeathBow:feat/gemma4-batched-decode-prep

Conversation

@FeathBow

Copy link
Copy Markdown
Collaborator

Description

Closes #891

A serving step that decodes one token for each of several requests needs its prep metadata per row: every request sits at its own absolute position, holds its own page table, and, for the sliding family, its own released front. The paged preps carry one request's metadata for the whole launch, so a batch can only be served one request at a time.

Both plain paged preps gain a per-token metadata form, where the page row is the window page_indptr cuts out of page_indices and a row outside that window traps rather than reading a page the request does not hold. The origin stays where it is real: the sliding family releases its front as the window moves, so its form takes a per-request origin; the global family never releases, so its absolute page index is its row index and it takes none. On top of those, one batched decode entry per head dim: at 256 it writes K and V into the pool per token, at 512 it carries the QK norm and the partial rotary for the family whose value is the key's scale-free branch.

Nothing calls the new entries yet. The scheduler and the batched serving step that consume them are the next change, which is also where a numeric gate first has something to measure.

Test Env

Single GPU (sm_89, x86_64), CUDA 12.9.

Verification

  • fmt; cargo build/clippy -D warnings for pegainfer-kernels and for pegainfer-gemma4 --features gemma4 (the feature build is what proves the existing single-request entries kept their signatures); the CI package set with --locked.
  • hd256_qk_rope_plain_smoke: 6 passed, including the new batched closed-form case. hd512_qk_rope_smoke: 6 passed, including the new batched case. hd256_window_prefill_reject: 1 passed.
  • hd256_decode_csr_trap is a manual gate, compiled by CI and run by hand: a window of two pages out of a one-page array passes every host check and has to be refused by the kernel. Run separately because a trap poisons the context. Run by hand here: 1 passed, and with the same fixture narrowed to the one legal page the test fails instead, so it is the kernel's refusal it measures.
  • pegainfer-gemma4 --features gemma4 --lib: 26 passed, 8 ignored (the checkpoint gates).

Type of Change

  • New feature (non-breaking change which adds functionality)

@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: 4f15c83bef

ℹ️ 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 +3128 to +3132
let table_rows = cos_max_pos.checked_mul(rotary_dim).ok_or_else(|| {
anyhow::anyhow!(
"hd256 paged decode prep cos_max_pos {cos_max_pos} * rotary_dim {rotary_dim} overflows"
)
})?;

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 Reject RoPE tables that overflow CUDA int indexing

When cos_max_pos * rotary_dim exceeds i32::MAX, this new wrapper accepts the tables because it only checks usize overflow, but the CUDA kernel indexes them with the signed-int expression pos * rotary_dim + d; sufficiently long contexts can therefore wrap the offset and read outside the RoPE caches. The existing paged-prefill wrappers explicitly pass the table extent through checked_i32, while both new decode wrappers omit that guard (the hd512 copy is at line 3599), so apply the same validation before launch.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch. Fixed both decode wrappers by applying checked_i32 to the complete RoPE table extent

Comment on lines +3055 to +3058
/// Per-request hd256 prep; `page_origins` is the released local-window front.
/// Invalid device metadata traps before the first paged-pool access.
#[allow(clippy::too_many_arguments)]
pub fn qkv_norm_rope_paged_decode_hd256_plain_into(

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 Document the new batched prep contract

This introduces scheduler-facing contracts whose central invariant—local rows carry a released-front origin while global rows remain absolute—is not recorded in project documentation; a repo-wide search for #891 and both new symbol names under docs/ returns no match, and this commit changes no docs. Add or update a Gemma 4 or kernels domain document with the rationale and next scheduler step (and index it if new), as required by the repository workflow.

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

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This slice should only adds unconsumed kernel entries; it does not introduce scheduler or serving behavior. The local/global metadata distinction is documented on the public API and in this PR, while the durable Gemma serving contract will be documented with the first batched-serving consumer, when it becomes reachable.

@FeathBow
FeathBow force-pushed the feat/gemma4-batched-decode-prep branch from 4f15c83 to c6d4721 Compare August 16, 2026 16:58
@FeathBow

Copy link
Copy Markdown
Collaborator Author

@codex review please

@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: c6d4721f63

ℹ️ 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-kernels/src/ops/attention.rs Outdated
Comment on lines +3153 to +3156
q.checked_extent("hd256 paged decode prep q")?;
q_out.checked_extent("hd256 paged decode prep q_out")?;
k.checked_extent("hd256 paged decode prep k")?;
v.checked_extent("hd256 paged decode prep v")?;

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 Reject decode tensors that exceed CUDA int indexing

When a batched decode input contains more than i32::MAX elements, these calls validate only the usize extent, while the CUDA kernel computes source and Q-output offsets with signed int expressions such as token * q_dim; the overflow can therefore read or write the wrong memory. The existing paged-prefill wrappers immediately above narrow the Q/K extents through checked_i32, but both new decode wrappers omit those guards (the hd512 copy is at lines 3624–3633), so apply the same checks before either launch.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch.

Signed-off-by: Feathbow <feathbow@gmail.com>
@FeathBow
FeathBow force-pushed the feat/gemma4-batched-decode-prep branch from c6d4721 to 3c96871 Compare August 16, 2026 17:50
@FeathBow

Copy link
Copy Markdown
Collaborator Author

@codex check again plz

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 3c968712bb

ℹ️ 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".

@FeathBow
FeathBow merged commit f190a00 into pegainfer-project:main Aug 16, 2026
13 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: prep entries carry one request's metadata per launch

1 participant