fix(kimi-k3): correct MFU accounting (FLOPs formula, config defaults) - #3792
Merged
HuiyingLi merged 2 commits intoSep 2, 2026
Merged
Conversation
… MFU
`get_flops_formula_for_hf_config` had no entry for `KimiK3TextConfig`, so
Kimi K3 fell through to the generic dense `transformer_flops`, whose MoE
branch probes `num_experts_per_tok` while the K3 config spells it
`num_experts_per_token`. The 2.78T hybrid MoE was therefore costed as a
dense 93-layer transformer and the recipe printed MFU at well under half of
the wall-clock value (13.7% measured -> ~6.0% printed at 64 nodes).
Add `kimi_k3_flops`, derived from the K3 config and the released modeling
code: 69 KDA + 24 gated-MLA layers read from `linear_attn_config`, the MLA
output gate, latent MoE (shared down/up projections around top-k experts in
`routed_expert_hidden_size`) plus the SiTU shared expert, one dense layer,
and the LM head. The KDA chunked kernel uses the Kimi Linear paper's own
count (6 T d_h^2 + 3 T C d_h + T C^2 per head, arXiv:2510.26692). With the
checkpoint's config.json values the formula gives 104.0B active matmul
parameters per token and 2.78T total, matching the model card ("104B
activated / 2.8T") and the tech report's Table 1 (104.2B / 2.78T).
Register both `KimiK3TextConfig` and the multimodal `KimiK3Config` wrapper
(text_config is used; the vision tower is not counted, as for other VL
entries). Tests pin the layer pattern, the model-card parameter counts, a
precomputed value, batch linearity, the >2x correction over the dense
fallback, and the exact cost of each optional block.
Signed-off-by: Yisong Li <yisongbetter@gmail.com>
…point `KimiK3TextConfig` defaulted to `num_attention_heads=56` and `intermediate_size=18432`, but the released `moonshotai/Kimi-K3` checkpoint (config.json text_config, tech report Table 1, and the safetensors shapes: `q_b_proj [18432, 1536]` = 96 heads x 192, `layers.0.mlp.gate_proj [33792, 7168]`) has 96 attention heads and a 33792-wide dense FFN. Every other default already matched. `from_pretrained` was unaffected because config.json overrides the defaults, but `from_config` -- used by the benchmark recipes and by random-init smoke configs -- built a lighter model (24 MLA layers with 56 heads, half-width dense layer: ~101.5B instead of ~104B active matmul parameters per token), so throughput numbers measured with it were not numbers for Kimi K3. Set the two defaults to the checkpoint values and pin the whole released text config (plus the implied MLA projection widths and the 69 KDA / 24 MLA layer pattern) in a unit test so the class cannot drift from the model it names again. Signed-off-by: Yisong Li <yisongbetter@gmail.com>
Contributor
|
/claude review |
Contributor
|
LGTM |
Contributor
|
/ok to test 1613a9f |
HuiyingLi
approved these changes
Sep 2, 2026
HuiyingLi
enabled auto-merge (squash)
September 2, 2026 20:01
yisongbetter
added a commit
to yisongbetter/Automodel
that referenced
this pull request
Sep 3, 2026
The 13.7% MFU figure was measured before NVIDIA-NeMo#3792 aligned the KimiK3TextConfig defaults with the released checkpoint (56-head MLA / 18432-wide dense FFN, ~101.5B active matmul parameters instead of 104.0B). Say so in the header, and point at the recipes/llm/benchmark.py entry that prints the recipe's own MFU, until a re-measurement on the aligned shape replaces the numbers. Signed-off-by: Yisong Li <yisongbetter@gmail.com>
yisongbetter
added a commit
to yisongbetter/Automodel
that referenced
this pull request
Sep 3, 2026
The 17.1% MFU figure predates NVIDIA-NeMo#3792's KimiK3TextConfig default alignment (56-head MLA / 18432-wide dense FFN, ~101.5B active matmul parameters instead of 104.0B). Record that in the header and point at the recipes/llm/benchmark.py entry that prints the recipe's own MFU, until a re-measurement on the aligned shape replaces the numbers. Signed-off-by: Yisong Li <yisongbetter@gmail.com>
This was referenced Sep 3, 2026
yisongbetter
added a commit
to yisongbetter/Automodel
that referenced
this pull request
Sep 3, 2026
The 13.7% MFU figure was measured before NVIDIA-NeMo#3792 aligned the KimiK3TextConfig defaults with the released checkpoint (56-head MLA / 18432-wide dense FFN, ~101.5B active matmul parameters instead of 104.0B). Say so in the header, and point at the recipes/llm/benchmark.py entry that prints the recipe's own MFU, until a re-measurement on the aligned shape replaces the numbers. Signed-off-by: Yisong Li <yisongbetter@gmail.com>
yisongbetter
added a commit
to yisongbetter/Automodel
that referenced
this pull request
Sep 3, 2026
The 17.1% MFU figure predates NVIDIA-NeMo#3792's KimiK3TextConfig default alignment (56-head MLA / 18432-wide dense FFN, ~101.5B active matmul parameters instead of 104.0B). Record that in the header and point at the recipes/llm/benchmark.py entry that prints the recipe's own MFU, until a re-measurement on the aligned shape replaces the numbers. Signed-off-by: Yisong Li <yisongbetter@gmail.com>
HuiyingLi
pushed a commit
that referenced
this pull request
Sep 3, 2026
…nc pins (#3779) * perf(kimi-k3): RMSNorm compile island and benchmark static-routing sync pins Two launch-tax reductions for the Kimi-K3 GB200 benchmark, measured at 256 GPUs (64x GB200 NVL72, pp8 x EP32, 1k rows x GBS 4096): 1. BackendConfig.compile_norm (default False): torch.compile the fp32 RMSNorm chain once per process, same lazy pattern as the merged compile_situ. At 256 GPUs this is worth +11% end-to-end (launch-bound regime: fusing the per-token norm chain pays in both busy time and inter-kernel gaps); the same change gains only +5.3% on a 2-node mini. 2. BackendConfig.benchmark_static_routing (default False, validated to require fake_balanced_gate with zero noise): under forced-balanced routing the per-microbatch routing metadata is constant, so the recurring device-to-host syncs on it are skipped after the first microbatch: tokens_per_expert.tolist() in GroupedExpertsTE, count_nonzero + the CPU tokens_per_expert copy in GroupedExpertsDeepEP, and the HybridEP num_permuted_tokens reset in the flex dispatcher (config field moe_benchmark_static_routing). Worth +2.4% together with the mask fixes below. Plus two unconditional fixes on the same hot path: - _update_linear_attn_mask returns early when attention_mask is None, skipping a per-microbatch D2H sync on cache_position[0] (both branches returned None for that input). - _make_causal_mask caches one upper-triangular mask per (dtype, device), grown on demand and sliced per call, instead of rebuilding the [S, S] mask every microbatch. The benchmark YAML enables both flags; its documented throughput moves from ~441 tok/s/GPU (12.2% MFU) to ~496 tok/s/GPU (13.7% MFU), measured wall-clock with the same formula as before. Mini-scale gates: loss parity across all steps with flags on/off; RMSNorm compile parity is covered by unit tests (allclose forward and backward). Signed-off-by: Yisong Li <yisongbetter@gmail.com> * test(moe): behavioral coverage for the static-routing m_splits cache Address the review bot's two observations: extract the TE-path split-size resolution into _resolve_m_splits (unchanged logic, now directly unit-testable on CPU) with tests asserting cache reuse under static routing and re-materialization without it; and read BackendConfig.benchmark_static_routing directly instead of a defensive getattr on the typed field. Signed-off-by: Yisong Li <yisongbetter@gmail.com> * docs(kimi-k3): state the measured model shape in the 1k benchmark header The 13.7% MFU figure was measured before #3792 aligned the KimiK3TextConfig defaults with the released checkpoint (56-head MLA / 18432-wide dense FFN, ~101.5B active matmul parameters instead of 104.0B). Say so in the header, and point at the recipes/llm/benchmark.py entry that prints the recipe's own MFU, until a re-measurement on the aligned shape replaces the numbers. Signed-off-by: Yisong Li <yisongbetter@gmail.com> --------- Signed-off-by: Yisong Li <yisongbetter@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do ?
Makes Automodel's Kimi K3 MFU accounting correct. Two independent defects made the reported MFU wrong for K3:
get_flops_formula_for_hf_confighad no entry forKimiK3TextConfig, so K3 fell through to the generic densetransformer_flops, whose MoE branch probesnum_experts_per_tokwhile the K3 config spells itnum_experts_per_token. The 2.78T hybrid MoE was costed as a dense 93-layer transformer at ~0.44× of its real FLOPs (a 64-node run that measures 13.7% MFU wall-clock printed ~6.0%).from_configbuilt a lighter model than the checkpoint.KimiK3TextConfigdefaulted tonum_attention_heads=56andintermediate_size=18432; the releasedmoonshotai/Kimi-K3has 96 heads and a 33792-wide dense FFN (config.json, safetensors shapes, tech report Table 1).from_pretrainedwas unaffected (config.json overrides), but the benchmark recipes and random-init smoke configs built ~101.5B instead of ~104B active matmul parameters per token, so their throughput numbers were not numbers for Kimi K3.Two commits, one per defect; they are independent (the formula's tests pass explicit config values).
Changelog
flops_utils.kimi_k3_flops: hybrid KDA / gated-MLA attention (layer pattern read fromlinear_attn_config, MLA output gate), latent MoE (routed_expert_hidden_sizedown/up projections around the top-k experts) + SiTU shared expert, dense layer(s) perfirst_k_dense_replace, LM head. KDA chunked kernel costed with the Kimi Linear paper's count (6 T d_h² + 3 T C d_h + T C²per head, arXiv:2510.26692)._kda_attention_per_layer_flopshelper mirrors_gdn_attention_per_layer_flops.KimiK3TextConfigand the multimodalKimiK3Configwrapper (text_configis used; the vision tower is not counted, as for the other VL entries).KimiK3TextConfigdefaults:num_attention_heads56 → 96,intermediate_size18432 → 33792; docstring states the defaults reproduce the released checkpoint.tests/unit_tests/utils/test_kimi_k3_flops.py(layer pattern, model-card parameter counts, precomputed values for the checkpoint and the pre-fix shapes, batch linearity, >2× correction over the dense fallback, exact cost of each optional block);tests/unit_tests/models/kimi_k3/test_config_defaults.py(pins the released text config, the linear-attention block, the 69 KDA / 24 MLA pattern, the MLA projection widths implied by the head count, the multimodal wrapper); a registration case intest_flops_utils_new_models.py.Verification
Cross-checked against the released checkpoint (
moonshotai/Kimi-K3config.json+modeling_kimi_linear.py+ safetensors headers:q_b_proj [18432, 1536]= 96 × 192,kv_b_proj [24576, 512],layers.0.mlp.gate_proj [33792, 7168]), the model card, and the K3 tech report (Table 1: 96 heads, 104.2B activated / 2.78T total):from_configbuilds the checkpoint's shape, and the formula counts 104.0B active matmul parameters per token, 2.78T total (within 0.2% of Table 1; the formula counts GEMM weights only — no router, norms or gate biases). Layer pattern 69 KDA + 24 gated MLA, 1 dense layer, 16/896 experts, 2 shared — all read from the config.6 × 104e9used for every published K3 number (the excess is the MLA attention BMMs and the KDA kernel).num_nextn_predict_layers: 0) and not modelled by Automodel, so it is not counted.tests/unit_tests/utils/247 passed (1 pre-existing environment failure intest_yaml_utils.py: needs/home/TestData);tests/unit_tests/models/kimi_k3/65 passed (1 environment failure: FLA kernels not installed);tests/unit_tests/recipes/llm/test_benchmark.py33 passed.ruff format --check/ruff checkclean on the changed files.nemo_automodel/recipes/llm/benchmark.py --config …, Kimi-K3 reduced to 24 layers / 64 experts, seq 4096, GBS 32, random init, the formula commit applied on upstreammain):TFLOPs/GPU: 21323.249904printed at setup =kimi_k3_flops(cfg, gbs=32, seq_len=4096)exactly (the dense fallback would have given 0.485× of that). Steady iterations 4.95 s → printedMFU: 23.93%; the wall-clock basis at the same iteration time (tokens/s/GPU × 6 × active params / 2.25e15) gives 23.74%, ratio 1.008.Average MFU: 23.17%over the 9 post-warmup iterations (two GC-slowed).Additional Information
train_ft.py); this fixes the FLOPs-per-token estimate and thefrom_configmodel shape.MFU:lines andAverage MFUcome fromrun_benchmark(), which only therecipes/llm/benchmark.py --config <yaml>entry (used by the L2 benchmark tests) runs; theautomodel <yaml>launcher in the yaml headers goes through the genericrun_train_validation_loop()and prints only the setup-timeTFLOPs/GPU.from_configusers: the K3 benchmark recipes (examples/llm_benchmark/kimi/kimi_k3_gb200*.yaml) and random-init smoke configs now build the real K3 shape — 24 MLA layers gain ~90M parameters each and the dense layer doubles, ≈ +2.5% FLOPs/token, ≈ +1 GiB/GPU at EP32×PP8 on 256 GPUs. Throughput numbers in those yaml headers were measured with the old shape and will be re-stamped in perf(kimi-k3): RMSNorm compile island and benchmark static-routing sync pins #3779 / perf(distributed): ring-pooled PP recv buffers and Kimi-K3 2k benchmark shape #3780 after a re-run.num_experts_per_tok, so other unregistered MoE configs with a different spelling are still under-costed — worth its own tiny fix.