Skip to content

[Puzzletron] Fix KV-cache dimension calculation for models with custom head_dim (#2482) - #2484

Closed
musi22 wants to merge 2 commits into
NVIDIA:feature/puzzletron_v2from
musi22:fix/puzzletron-kv-dim-2482
Closed

musi22 wants to merge 2 commits into
NVIDIA:feature/puzzletron_v2from
musi22:fix/puzzletron-kv-dim-2482

Conversation

@musi22

@musi22 musi22 commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Description

Fixes #2482

Currently, calculate_kv_dim computes head_size = n_embd // n_head. For architectures where head_dim is decoupled from embedding division (such as Mistral Small 24B where head_dim = 128 vs 5120 // 32 = 160, or gpt-oss 20B where head_dim = 64 vs 2880 // 64 = 45), this leads to inaccurate KV-cache memory and additive metric calculations.

Changes

  1. modelopt/torch/puzzletron/utils/misc.py:
    • Added optional head_dim: int | None = None parameter to calculate_kv_dim.
    • If head_dim is provided, it is used directly; otherwise, it falls back to n_embd // n_head to maintain complete backward compatibility.
  2. modelopt/torch/puzzletron/subblock_stats/calc_subblock_params_and_memory.py:
    • In both calculate_additive_metrics and calculate_attention_memory, resolved effective_head_dim following precedence: subblock_config.qk_head_dim > model_config.head_dim (via _language_model_attr) > fallback n_embd // n_head.
    • Hardened _language_model_attr helper to safely handle configs when descriptor lacks get_language_model_config.
  3. Tests:
    • Added tests/unit/torch/puzzletron/test_kv_dim_calculation.py with 6 unit tests validating Mistral Small 24B, gpt-oss 20B, default fallback, and precedence behavior.

Testing

  • Ran unit test suite:
    pytest tests/unit/torch/puzzletron/test_kv_dim_calculation.py tests/unit/torch/puzzletron/test_additive_subblock_metrics.py tests/unit/torch/puzzletron/test_attention_runtime_memory.py
    Result: 20 passed.
  • Ruff checks and format passed.

Summary by CodeRabbit

  • Bug Fixes

    • Improved attention KV-dimension calculations by honoring configured query/key head dimensions when available.
    • Added reliable fallbacks for models that provide general head-dimension or embedding configuration.
    • Improved KV-cache memory and bytes-per-token estimates across supported model configurations.
  • Tests

    • Added coverage for explicit and inferred head dimensions, configuration precedence, and attention-memory calculations.

…ulation (NVIDIA#2482)

Signed-off-by: musi22 <shawrashmi7@gmail.com>
@copy-pr-bot

copy-pr-bot Bot commented Sep 20, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: NVIDIA/Model-Optimizer/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: abaa9560-4d50-415d-94b8-180e62f7aab8

📥 Commits

Reviewing files that changed from the base of the PR and between c924530 and c36dac7.

📒 Files selected for processing (2)
  • modelopt/torch/puzzletron/subblock_stats/calc_subblock_params_and_memory.py
  • tests/unit/torch/puzzletron/test_kv_dim_calculation.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unit/torch/puzzletron/test_kv_dim_calculation.py
  • modelopt/torch/puzzletron/subblock_stats/calc_subblock_params_and_memory.py

Included review availability: Your plan provides up to 12 included reviews per hour; 11 remain after this review.


📝 Walkthrough

Walkthrough

The change updates KV-dimension calculation to use configured head dimensions, adds configuration fallbacks, supports descriptors without language-model configuration methods, and adds tests for KV-cache metrics.

Changes

KV dimension resolution

Layer / File(s) Summary
Head dimension resolution and metric integration
modelopt/torch/puzzletron/utils/misc.py, modelopt/torch/puzzletron/subblock_stats/calc_subblock_params_and_memory.py
calculate_kv_dim accepts optional dimensions and prioritizes head_dim. Attention metrics use qk_head_dim, model head_dim, or inferred dimensions. Configuration lookup supports descriptors without get_language_model_config.
KV metric coverage
tests/unit/torch/puzzletron/test_kv_dim_calculation.py
Tests cover explicit and inferred dimensions, KV-cache memory, additive metrics, and dimension precedence.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR meets the coding requirements in #2482. calculate_kv_dim accepts head_dim and uses n_embd // n_head when no explicit dimension exists. calculate_attention_memory and `calculate_additive…
Out of Scope Changes check ✅ Passed The changes remain within #2482. The _language_model_attr safeguard supports model configuration lookup for the required head_dim resolution. The compatibility update and the added tests directly …
Docstring Coverage ✅ Passed Docstring coverage is 90.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 3 files.
Security Anti-Patterns ✅ Passed The pull request adds only KV-dimension logic, documentation, and tests. The authoritative diff adds no prohibited torch.load(..., weights_only=False), numpy.load(..., allow_pickle=True), hardcode…
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: correcting KV-cache dimension calculations for models with a custom head_dim.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

… 100% coverage

Signed-off-by: musi22 <shawrashmi7@gmail.com>
@musi22 musi22 changed the title [Puzzletron] Fix KV-cache dimension calculation for models with custom head_dim (#2482)fix(puzzletron): respect explicit head_dim in KV-cache dimension calc… [Puzzletron] Fix KV-cache dimension calculation for models with custom head_dim (#2482) Oct 8, 2026
soksof added a commit to soksof/Model-Optimizer that referenced this pull request Oct 25, 2026
calculate_kv_dim computed the head size as n_embd // n_head, so
calculate_attention_memory mispriced the KV cache of every attention
subblock whenever the model sets head_dim independently (Mistral-Small,
GPT-OSS, Gemma-3), and for every candidate with a pruned hidden size,
since subblock statistics are computed per candidate hidden_size with the
parent's num_attention_heads. Pruning hidden_size leaves head_dim
unchanged, so the head dimension is now read from the language-model
config, with n_embd // n_head as the fallback when it is not set.

Fixes NVIDIA#2482. Based on the fix in NVIDIA#2484, ported to the current main.

Co-authored-by: musi22 <shawrashmi7@gmail.com>
Signed-off-by: Sokratis Sofianopoulos <sokratis.sofianopoulos@gmail.com>
@musi22

musi22 commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Closing in favor of #3009 which ports this fix to current main with co-authorship and additional regression tests.

@musi22 musi22 closed this Oct 25, 2026
soksof added a commit to soksof/Model-Optimizer that referenced this pull request Oct 25, 2026
calculate_kv_dim computed the head size as n_embd // n_head, so
calculate_attention_memory mispriced the KV cache of every attention
subblock whenever the model sets head_dim independently (Mistral-Small,
GPT-OSS, Gemma-3), and for every candidate with a pruned hidden size,
since subblock statistics are computed per candidate hidden_size with the
parent's num_attention_heads. Pruning hidden_size leaves head_dim
unchanged, so the head dimension is now read from the language-model
config, with n_embd // n_head as the fallback when it is not set.

Fixes NVIDIA#2482. Based on the fix in NVIDIA#2484, ported to the current main.

Co-authored-by: musi22 <shawrashmi7@gmail.com>
Signed-off-by: GitHub <noreply@github.com>
soksof added a commit to soksof/Model-Optimizer that referenced this pull request Oct 25, 2026
calculate_kv_dim computed the head size as n_embd // n_head, so
calculate_attention_memory mispriced the KV cache of every attention
subblock whenever the model sets head_dim independently (Mistral-Small,
GPT-OSS, Gemma-3), and for every candidate with a pruned hidden size,
since subblock statistics are computed per candidate hidden_size with the
parent's num_attention_heads. Pruning hidden_size leaves head_dim
unchanged, so the head dimension is now read from the language-model
config, with n_embd // n_head as the fallback when it is not set.

Fixes NVIDIA#2482. Based on the fix in NVIDIA#2484, ported to the current main.

Co-authored-by: musi22 <shawrashmi7@gmail.com>
Signed-off-by: Sokratis Sofianopoulos <s_sofian@athenarc.gr>
kevalmorabia97 added a commit that referenced this pull request Oct 26, 2026
#3009)

### What does this PR do?

Type of change: Bug fix

`calculate_kv_dim` computes the head size as `n_embd // n_head` and does
not read `head_dim`, so `calculate_attention_memory` misprices the KV
cache of every attention subblock in two cases:

- models that set `head_dim` independently of `hidden_size /
num_attention_heads`, e.g. Mistral-Small-24B (128 against 160, 25% too
high), gpt-oss-20b (64 against 45, 30% too low) and Gemma-3-12B (256
against 240)
- every candidate with a pruned hidden size, for all models: subblock
statistics are computed per candidate `hidden_size` with the parent's
`num_attention_heads`, while pruning `hidden_size` leaves `head_dim`
unchanged. For example, Qwen3-8B with `hidden_size` pruned to 3072 gives
3072 // 32 = 96 instead of 128, i.e. a KV cache 25% too low

The head dimension is now read from the language-model config
(`descriptor.get_language_model_config(model_config).head_dim`), with
`n_embd // n_head` as the fallback when it is not set.
`calculate_kv_dim` gets an optional `head_dim` argument, so existing
callers are unchanged.

This is the fix of #2484 ported to the current `main` (that PR was based
on `feature/puzzletron_v2`, and `main` has since renamed `num_kv_heads`
to `num_key_value_heads`), with @musi22 as co-author, as suggested in
#2482. On `main` the KV-cache memory is the only consumer of
`calculate_kv_dim`. On `feature/puzzletron_v2`, the attention FLOP
estimate in `calculate_additive_metrics` still uses `qk_head_dim or
(n_embd // n_head)` and should use the same resolution; this is not
included here, given that the function does not exist on `main`.

### Usage

No API change. `calculate_kv_dim(num_key_value_heads, n_head, n_embd,
head_dim=None)` keeps its previous behaviour when `head_dim` is not
given.

### Testing

- New `tests/unit/torch/puzzletron/test_kv_dim.py`, 5 cases, 0.2 s in
total:
- `calculate_kv_dim` with an explicit `head_dim` smaller (Mistral-Small)
and larger (gpt-oss) than `n_embd // n_head`, and the fallback without
`head_dim`
- `calculate_attention_memory` with `head_dim=128` at `n_embd` 5120 and
3840: the KV-cache memory is the same in both cases, i.e. pruning
`hidden_size` does not change it. `calculate_subblock_params` is patched
to 0, since the weight count is not part of the KV cache and would need
a real model
- All 5 cases fail without the fix (the KV cache comes out as 0.156 and
0.117 MiB instead of 0.125) and pass with it
- `pytest tests/unit/torch/puzzletron`: 49 passed, on CPU (torch 2.14,
transformers 5.17, Python 3.13)
- `ruff format` and `ruff check` (0.15.20) on the changed files: no new
findings (the 4 `PLR0917` findings in
`calc_subblock_params_and_memory.py` are present on `main` as well)
- Not run: GPU tests, and an end-to-end Puzzletron search

### Before your PR is "*Ready for review*"

- Is this change backward compatible?: ✅ (the new argument is optional;
subblock statistics computed with earlier versions change for the
affected models and should be recomputed)
- If you copied code from any other sources or added a new PIP
dependency, did you follow guidance in `CONTRIBUTING.md`: ✅ (logic from
#2484 by @musi22, credited as co-author; no new dependency)
- Did you write any new necessary tests?: ✅
- Did you update
[[Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?:
✅ (Bug Fixes, 0.49.0)
- Did you get Claude approval on this PR?: ❌ (to be run after opening)

### Additional Information

Fixes #2482. Supersedes #2484 and #2485.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Bug Fixes**
* KV-cache memory estimates now use the configured attention head size
when available, improving accuracy for models with explicit head-size
settings and when embedding dimensions are pruned. If no head size is
configured, estimates continue to use the inferred value.
* Recompute existing subblock statistics to reflect the corrected memory
estimates and ensure pruning results use up-to-date values.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Sokratis Sofianopoulos <s_sofian@athenarc.gr>
Signed-off-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
Co-authored-by: musi22 <shawrashmi7@gmail.com>
Co-authored-by: Keval Morabia <28916987+kevalmorabia97@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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