Skip to content

[Bugfix][CPU] Take an attention group's query head count from its layers - #51852

Merged
bigPYJ1151 merged 4 commits into
vllm-project:mainfrom
ganeshr10:fix-cpu-attn-per-layer-head-counts
Aug 17, 2026
Merged

[Bugfix][CPU] Take an attention group's query head count from its layers#51852
bigPYJ1151 merged 4 commits into
vllm-project:mainfrom
ganeshr10:fix-cpu-attn-per-layer-head-counts

Conversation

@ganeshr10

@ganeshr10 ganeshr10 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Purpose

CPUAttentionMetadataBuilder sizes the split-KV scratchpad from the model-wide query head count, but the kernel runs with each layer's own count. so models that vary it per layer (e.g. Laguna) index past the end of the scratchpad and
the decode segfaults or hangs. Attention groups are keyed on num_heads_q, so take the count from the group's layers via get_num_attention_heads_from_layers(), as triton_attn and flashinfer already do. Models with a uniform count are unaffected.

Change-Id: I671eadc2b5601f1a3af12f39391657a11df1c9f7

Test Plan

  • pytest tests/v1/attention/test_group_head_counts.py — new unit test, mirroring test_group_sliding_window.py: uniform heads, Laguna-style alternating heads, and a default that is not the most common count.
  • A tiny Laguna config with per-layer heads [8, 16, 8, 16] against 2 KV heads and random weights (load_format=dummy, so no checkpoint is needed), decoding 512 tokens so split-KV reduction engages. Run both on unpatched main and with this change.
  • Laguna-XS-2.1-INT4 end to end on CPU with TP=2: a 300-token decode, plus GSM8K 5-shot over the full 1319 prompts via lm_eval.
Reproducer
import json, os, tempfile
from vllm import LLM, SamplingParams

CONFIG = {
    "architectures": ["LagunaForCausalLM"], "model_type": "laguna",
    "hidden_size": 512, "intermediate_size": 1024, "num_hidden_layers": 4,
    "num_attention_heads": 8, "num_attention_heads_per_layer": [8, 16, 8, 16],
    "num_key_value_heads": 2, "head_dim": 64, "max_position_embeddings": 4096,
    "rms_norm_eps": 1e-6, "rope_theta": 500000.0, "tie_word_embeddings": False,
    "torch_dtype": "bfloat16", "mlp_only_layers": [0, 1, 2, 3],
    "num_experts": 4, "num_experts_per_tok": 2, "moe_intermediate_size": 128,
    "shared_expert_intermediate_size": 128,
}

model_dir = tempfile.mkdtemp()
with open(os.path.join(model_dir, "config.json"), "w") as f:
    json.dump(CONFIG, f)

llm = LLM(model=model_dir, tokenizer="<any tokenizer>", load_format="dummy",
          dtype="bfloat16", max_model_len=2048, enforce_eager=True)
out = llm.generate(["context " * 64],
                   SamplingParams(max_tokens=512, temperature=0.0, ignore_eos=True))
print("SURVIVED", len(out[0].outputs[0].token_ids))

Test Result

  • Unit tests: 6 passed (3 new, plus the 3 existing sliding-window cases).

  • Reproducer: hangs on current main and is killed at the timeout; passes in 1.5s with this change.

  • Laguna-XS 300-token decode: previously segfaulted or hung, now completes in 47s with coherent output.

  • GSM8K 5-shot, full 1319 prompts. Before this change the same run died with an execute_model timeout.

    Filter exact_match stderr
    flexible-extract 0.9014 ±0.0082
    strict-match 0.8908 ±0.0086

Instrumenting the scratchpad bounds shows the overrun directly on unpatched main: the region holds 6336 bytes per KV head while the split-KV write path needs 8384, because sizing assumes 6 query heads per KV head (48 heads / 8 KV heads) and the 64-head layers address it with a stride of 8.

[SCRATCHPAD-SIZING]   split_num=2 max_tile=6 q_heads=24 kv_heads=4 q_per_kv=6 per_kv_head=6336
[SCRATCHPAD-OVERFLOW] per_head_need=8384 per_kv_head=6336 q_head_tile_size=8

Models whose layers share one head count keep the existing single-blob path, so there is no extra allocation or lookup for them.


Essential Elements of an Effective PR Description Checklist
  • The purpose of the PR, such as "Fix some issue (link existing issues this PR will resolve)".
  • The test plan, such as providing test command.
  • The test results, such as pasting the results comparison before and after, or e2e results
  • (Optional) The necessary documentation update, such as updating supported_models.md and examples for a new model.

@mergify mergify Bot added cpu Related to CPU backends bug Something isn't working labels Aug 11, 2026
@ganeshr10
ganeshr10 marked this pull request as ready for review August 12, 2026 07:55

@claude claude 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.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@bigPYJ1151 bigPYJ1151 added the verified Run pre-commit for new contributors without triggering other tests label Aug 13, 2026
@mergify

mergify Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Hi @ganeshr10, the pre-commit checks have failed. Please run:

uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

Comment thread tests/v1/attention/test_group_head_counts.py Outdated
Comment thread vllm/v1/attention/backends/cpu_attn.py Outdated
Comment thread vllm/v1/attention/backends/cpu_attn.py Outdated
@ganeshr10
ganeshr10 force-pushed the fix-cpu-attn-per-layer-head-counts branch from 33cb73d to 839fde8 Compare August 17, 2026 06:09
@mergify

mergify Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Hi @ganeshr10, the pre-commit checks have failed. Please run:

uv pip install pre-commit>=4.5.1
pre-commit install
pre-commit run --all-files

Then, commit the changes and push to your branch.

For future commits, pre-commit will run automatically on changed files before each commit.

@ganeshr10 ganeshr10 changed the title [Bugfix] Give CPU attention layers their own metadata when head counts differ Bugfix][CPU] Take an attention group's query head count from its layers Aug 17, 2026
@ganeshr10 ganeshr10 changed the title Bugfix][CPU] Take an attention group's query head count from its layers {Bugfix][CPU] Take an attention group's query head count from its layers Aug 17, 2026
@ganeshr10 ganeshr10 changed the title {Bugfix][CPU] Take an attention group's query head count from its layers [Bugfix][CPU] Take an attention group's query head count from its layers Aug 17, 2026
ganeshr10 and others added 3 commits August 17, 2026 01:19
…s differ

The CPU backend sizes one scheduler metadata blob per KV cache group from the
model-wide query head count, so models that vary it per layer (e.g. Laguna)
overrun the split-KV scratchpad and either segfault or hang. Build metadata per
distinct head count and let each layer select the one matching its own.

Signed-off-by: Ganesh R <Ganesh.R@amd.com>
Change-Id: I671eadc2b5601f1a3af12f39391657a11df1c9f7
The layers a group covers are known by the time the builder is built, so
read them there rather than on the first build(). This also drops the
Optional from window_size, which mypy could not narrow inside the nested
scheduler metadata helper.

Run the head count test only on CPU, and in CPU CI.

Signed-off-by: R <Ganesh.R@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Change-Id: Iad3cd2ff8ad7dbc4101e5341c5673e2b0b300e9e
Signed-off-by: R <Ganesh.R@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Attention groups are keyed on num_heads_q, so the layers in a group always
agree on it and no single metadata blob has to cover several counts. Read
that count with get_num_attention_heads_from_layers, which triton_attn and
flashinfer already use for the same reason, and drop the per-count metadata
dict.

Signed-off-by: R <Ganesh.R@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Change-Id: I80456459171fef0edd9d10430d98ebc9e9b56131
@ganeshr10
ganeshr10 force-pushed the fix-cpu-attn-per-layer-head-counts branch from 839fde8 to f307f46 Compare August 17, 2026 07:23

@bigPYJ1151 bigPYJ1151 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! LGTM

@bigPYJ1151

Copy link
Copy Markdown
Member

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #84171 for commit f307f4688309.

@bigPYJ1151

Copy link
Copy Markdown
Member

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #84175 for commit fcad8f0708a2.

@bigPYJ1151
bigPYJ1151 merged commit f27ae25 into vllm-project:main Aug 17, 2026
101 checks passed
Alessandra005 pushed a commit to Alessandra005/vllm that referenced this pull request Aug 17, 2026
…ers (vllm-project#51852)

Signed-off-by: Ganesh R <Ganesh.R@amd.com>
Signed-off-by: R <Ganesh.R@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Li, Jiang <jiang1.li@intel.com>
Signed-off-by: Alessandra005 <aurib032@fiu.edu>
@khluu khluu added this to the v0.28.0 cherry picks milestone Aug 18, 2026
khluu pushed a commit that referenced this pull request Aug 20, 2026
…ers (#51852)

Signed-off-by: Ganesh R <Ganesh.R@amd.com>
Signed-off-by: R <Ganesh.R@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Li, Jiang <jiang1.li@intel.com>
(cherry picked from commit f27ae25)
zyp2014 pushed a commit to zyp2014/vllm that referenced this pull request Aug 21, 2026
…ers (vllm-project#51852)

Signed-off-by: Ganesh R <Ganesh.R@amd.com>
Signed-off-by: R <Ganesh.R@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Li, Jiang <jiang1.li@intel.com>
wyettzeng pushed a commit to wyettzeng/vllm that referenced this pull request Aug 21, 2026
…ers (vllm-project#51852)

Signed-off-by: Ganesh R <Ganesh.R@amd.com>
Signed-off-by: R <Ganesh.R@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Li, Jiang <jiang1.li@intel.com>
Signed-off-by: Wyett <wyettzeng@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ci/build cpu Related to CPU backends verified Run pre-commit for new contributors without triggering other tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants