Repository navigation
fix: use explicit head dim for Puzzletron KV cache #2485
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -357,7 +357,15 @@ def calculate_attention_memory( | |
| ): | ||
| seq_len = min(seq_len, attention_chunk_size) | ||
|
|
||
| kv_dim = calculate_kv_dim(attention_config.num_key_value_heads, n_head, n_embd) | ||
| lm_config = descriptor.get_language_model_config(model_config) | ||
| head_dim = getattr(attention_config, "qk_head_dim", None) or getattr(lm_config, "head_dim", None) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [IMPORTANT Correctness]
There is no Why it matters: it reads as if per-subblock head dims are honored during the Puzzletron search, which is the case a reviewer would most want to trust here. Anyone later adding a real per-subblock head dim will assume this line already wires it up. It is also silently unreachable — no error surfaces, the value is just wrong-by-omission if a subblock ever does override the head dim. Suggested fix: drop the Separately, prefer |
||
|
|
||
| kv_dim = calculate_kv_dim( | ||
| attention_config.num_key_value_heads, | ||
| n_head, | ||
| n_embd, | ||
| head_dim=head_dim, | ||
| ) | ||
| total_num_tokens = seq_len * (batch_size + prefill_queue_size) | ||
| kv_cache_size = total_num_tokens * kv_dim | ||
| query_prefill_size = seq_len * n_embd if allocate_prefill_query else 0 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| from modelopt.torch.puzzletron.utils.misc import calculate_kv_dim | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [IMPORTANT Compatibility] This new file is missing the Apache-2.0 license header, which will fail the repo's pre-commit gate.
Every sibling in this directory already has it ( # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.Running |
||
|
|
||
|
|
||
| def test_calculate_kv_dim_with_explicit_head_dim(): | ||
| assert calculate_kv_dim( | ||
| num_key_value_heads=8, | ||
| n_head=32, | ||
| n_embd=5120, | ||
| head_dim=128, | ||
| ) == 2048 | ||
|
|
||
|
|
||
| def test_calculate_kv_dim_falls_back_to_hidden_size(): | ||
| assert calculate_kv_dim( | ||
| num_key_value_heads=8, | ||
| n_head=32, | ||
| n_embd=4096, | ||
| ) == 2048 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[CRITICAL Algorithm] Reading
head_dimfrom the teacher config overrides the swept candidate hidden size, regressing KV-cache estimates for every hidden-size pruning candidate.n_embdhere is not the teacher's hidden size — it is a swept candidate value. Tracing upstream:calc_subblock_stats.py:319-321buildsmodel_hidden_sizes = model_hidden_sizes + [lm_config.hidden_size]("add a teacher model hidden size"), i.e. a list of candidate hidden sizes plus the teacher's.calc_subblock_stats.py:326iteratesproduct(batch_sizes, data_types, model_hidden_sizes).calc_subblock_stats.py:345-346passesn_embd=model_hidden_size(varies per iteration) andn_head=lm_config.num_attention_heads(fixed).model_configstays the fixed teacher config throughout, solm_config.head_dimis a constant across the whole sweep.The old
head_size = n_embd // n_headcorrectly tracked each candidate hidden size. The new code pinshead_sizeto the teacher'shead_dimfor all candidates.Concrete failure: teacher with
hidden_size=5120,num_attention_heads=40,head_dim=128,num_key_value_heads=8. For the candidatemodel_hidden_size=2560, the correct head size is2560 // 40 = 64→kv_dim = 2*8*64 = 1024. This code yieldshead_dim=128→kv_dim = 2048, a 2× overestimate ofkv_cache_size, which flows straight intokv_cache_memory_mibandmemory_mib. Those are the memory costs the Puzzletron search optimizes against, so every hidden-size-reduced candidate is now penalized as twice as KV-expensive as it is, skewing candidate selection. Note this hits plain Llama teachers too: modernLlamaConfigalways populateshead_dim, sogetattr(lm_config, "head_dim", None)returns a value and the old derivation is never reached.The fix for #2482 needs to distinguish the two cases:
head_dimis an independent model property only when it is not simplyhidden_size // num_attention_heads(Qwen3, gpt-oss, etc.). When it is derived, it must keep trackingn_embd:A regression test that pins this would be valuable: assert
kv_cache_memory_mibstill halves whenn_embdhalves for a teacher whosehead_dim == hidden_size // num_heads. The current unit tests only exercisecalculate_kv_dimin isolation, so they cannot catch this.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the CRITICAL finding in the automated review, I think the premise doesn't hold for how Puzzletron prunes width. EmbeddingPruningSpec.update_config_object rewrites only hidden_size; head_dim, num_attention_heads and num_key_value_heads are left as in the teacher. A hidden-size-pruned child therefore builds k_proj/v_proj with unchanged output dimensions, and its KV cache per token is 2 * num_key_value_heads * head_dim, independent of the candidate hidden size. Using the teacher's head_dim for every candidate in the sweep is the correct value, not an overestimate. The previous n_embd // n_head understated KV for width candidates too, since it made KV shrink with hidden size. The suggested workaround (fall back to n_embd // n_head when head_dim == hidden_size // num_attention_heads) would bring that back for Llama-style teachers. A useful regression test would be the opposite of the one proposed: kv_cache_memory_mib stays constant when n_embd is reduced with heads unchanged.
Separately, qk_head_dim does not exist on AttentionConfig on main but does on feature/puzzletron_v2, where calculate_additive_metrics already reads it, so a port of this fix to v2 could honour it there.