Skip to content

[FEATURE] HY V4: optional fp8 KV cache dequant to BF16 for sparse att… - #116

Open
alexanderbin123 wants to merge 1 commit into
v0.25.1from
feat/hyv4-fp8-kv-dequant-decode
Open

alexanderbin123 wants to merge 1 commit into
v0.25.1from
feat/hyv4-fp8-kv-dequant-decode

Conversation

@alexanderbin123

@alexanderbin123 alexanderbin123 commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

What

Adds an opt-in path for HY V4 sparse MLA, gated by VLLM_HCU_HYV4_FP8_KV_DEQUANT, that dequantizes the fp8 KV cache to BF16 and runs the geometry-agnostic BF16 sparse kernel (flash_mla_sparse_fwd), using a fused Triton gather+dequant kernel.

Why

HY V4's sparse MLA runs in mixed-batch mode (local num_heads=8 in TP8 < 32), so all tokens — prefill and decode — route through the single fp8 FlashMLA kernel. That kernel hardcodes DeepSeek's fp8_ds_mla geometry (pe_dim==64). When the env var is set, we intercept that call and run the BF16 sparse kernel on an upconverted cache instead, covering both prefill and decode.

How

  • fp8_kv_dequant.py — a fused Triton gather+dequant kernel over the 656-byte fp8_ds_mla layout (512 fp8 NoPE + 4 fp32 tile scales + 64 bf16 RoPE). It does not upconvert the whole pool: the kernel is launched over exactly num_tokens * topk programs, gathering and dequantizing only the topk-selected slots into a compact (num_tokens*topk, 576) BF16 buffer, and returns the sparse indices remapped to that buffer's rows (t*topk+k, -1 preserved). Gathering only the topk slots is ~60× less work than upconverting the whole pool (≈16K slots vs ≈1M slots/layer). topk is constant and num_tokens is fixed per graph batch, so the output shape is static and the decode path stays CUDA-graph-capturable. flash_mla_sparse_fwd masks the -1 indices natively.
  • hcu_sparse.py — intercepts _fp8_flash_mla_kernel when the env var is set and dispatches to the BF16 sparse kernel on the gathered/dequantized cache, passing the remapped indices.
  • envs.py — declares VLLM_HCU_HYV4_FP8_KV_DEQUANT (default False).

Testing

  • TP8 smoke test on Hy4-preview-Channel-FP8-w8a8 with the env var enabled: warmup and both PIECEWISE and FULL CUDA-graph capture pass, coherent generation, and the "BF16 sparse prefill kernel" log line (emitted during FULL capture) confirms the dequant path is taken.
  • Kernel numerics checked bit-exact against an independent torch reference on realistic fp8 data.

Related

A LightOp-backed variant of the same env-gated feature is #117 (feat/hyv4-fp8-kv-dequant-lightop) — hcu_sparse.py and envs.py are identical; only fp8_kv_dequant.py differs (LightOp vendor op vs this Triton kernel). Pick one to merge.

…ention

HY V4's sparse MLA runs in mixed-batch mode (num_heads=8 in TP8 < 32), so
all tokens (prefill and decode) route through the fp8 FlashMLA kernel. That
kernel hardcodes DeepSeek's fp8_ds_mla geometry. This adds an opt-in path,
gated by VLLM_HCU_HYV4_FP8_KV_DEQUANT, that dequantizes the fp8 KV cache to
BF16 and runs the geometry-agnostic BF16 sparse kernel (flash_mla_sparse_fwd)
instead, covering both prefill and decode.

- fp8_kv_dequant.py: a fused Triton gather+dequant kernel over the 656-byte
  fp8_ds_mla layout (512 fp8 NoPE + 4 fp32 tile scales + 64 bf16 RoPE). It
  reads only the topk-selected slots and writes a compact
  (num_tokens*topk, 576) BF16 buffer, and returns the sparse indices remapped
  to that buffer's rows (-1 preserved). Gathering only the topk slots is
  ~60x less work than upconverting the whole pool; topk is constant and
  num_tokens is fixed per graph batch, so the output shape is static and the
  decode path stays CUDA-graph-capturable. flash_mla_sparse_fwd masks the
  -1 indices natively.
- hcu_sparse.py: intercept _fp8_flash_mla_kernel when the env var is set and
  dispatch to the BF16 sparse kernel on the gathered/dequantized cache,
  passing the remapped indices.
- envs.py: declare VLLM_HCU_HYV4_FP8_KV_DEQUANT (default False).

Verified with a TP8 smoke test on Hy4-preview-Channel-FP8-w8a8 with the env
var enabled: warmup and both PIECEWISE and FULL CUDA-graph capture pass,
coherent generation, and the "BF16 sparse prefill kernel" log line (emitted
during FULL capture) confirms the dequant path is taken. Kernel numerics
also checked bit-exact against an independent torch reference on realistic
fp8 data.
@alexanderbin123
alexanderbin123 force-pushed the feat/hyv4-fp8-kv-dequant-decode branch from 8b24446 to c222098 Compare September 15, 2026 07:45
@hygon-ai-ai-reviewer

Copy link
Copy Markdown

AI CI 失败分析

工作流:HCU PR CI
状态:失败

总结

  • Static gate and test selection:本批有可逐字核对的直接失败:pytest 断言 '! -uid "$uid"' 未出现在测试读取的 workflow 文件源码中(L298),错误定位 tests/patch/test_hcu_ci_selector.py:801 AssertionError(L301);L313 为对应 FAILED 摘要,L314 显示 1 failed, 2282 passed,仅此一例失败,exit code 1 为其结果。

  • ci-gate:尚未定位可核对的直接异常。

与本次改动的关系

无法确定:本批提供的 diff 信息显示 0/3 文件有可用 patch,无法核对 PR 是否修改了断言涉及的 workflow 文件或该测试本身;Run SHA 与 PR SHA 不同也不能单独证明关联或无关。

建议处理

  1. 先人工核对 PR 是否改动断言所列的 .github/workflows 文件或 tests/patch/test_hcu_ci_selector.py:若有意调整 workspace 修复逻辑,则同步更新测试预期;若非本意,检查对应 workflow 中该字符串是否被移除并恢复。

@hygon-ai-ai-reviewer hygon-ai-ai-reviewer 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.

AI Review

🟢 未发现有明确证据的问题

在已提供并完成审查的 diff 范围内未形成可确认问题。

变更概览

本次变更主要包含:

  • 新增可选路径:环境变量开启时,将 fp8_ds_mla KV 缓存中仅 sparse kernel 实际读取的 topk 槽位反量化为 BF16 紧凑缓冲,并把注意力计算改走几何无关的 BF16 稀疏核,从而绕过 fp8 核硬编码的布局约束,同时保持 CUDA-graph 可捕获的固定输出形状。
  • 在环境变量定义中新增布尔开关 VLLM_HCU_HYV4_FP8_KV_DEQUANT(默认关闭),用于控制 HY V4 的 fp8 KV cache 在稀疏注意力前反量化为 BF16 而非调用写死 pe_dim==64 几何的 fp8 FlashMLA 内核,并同时覆盖 prefill 与 decode 路径。
文件审查摘要
文件 变更 审查结果
vllm_hcu/models/hy_v4/fp8_kv_dequant.py 新增 · +146/-0
vllm_hcu/models/hy_v4/hcu_sparse.py 修改 · +49/-1
vllm_hcu/platforms/envs.py 修改 · +9/-0
审查信息
  • 变更统计:3 个文件,+204/-1。
  • 覆盖情况:共 3 个文件,已完整审查 3 个。
  • 候选问题:0 项;证据复核过滤:0 项;发布前敏感信息保护:0 项。
  • 本服务只审查 GitHub 提供的 PR diff,未执行代码或重跑测试;结论仍需维护者核验。

@hygon-ai-ai-reviewer

Copy link
Copy Markdown

AI CI 失败分析

工作流:HCU PR CI
状态:失败

总结

  • Static gate and test selection:直接失败为单测断言:tests/patch/test_hcu_ci_selector.py:801 断言 '! -uid "$uid"' 应存在于 release-docker-image.yml / validate-docker-image.yml 中,实际读取内容不含该串。L316 显示 1 failed, 2282 passed;L317 的退出码 1 是该失败的汇总信号。

  • ci-gate:门禁脚本检测到 STATIC_RESULT=failure,命中其内部失败分支,输出 Static gate or selector failed. 并以 exit 1 结束(L43/L45/L46),属该汇总步骤按设计拦截上游检查;static 检查本身失败的具体原因不在本批日志内,无法确定。

与本次改动的关系

无法确定:已定位的直接异常对本次改动的关联判断一致。

建议处理

  1. 人工核对 PR 是否涉及上述两个工作流或该测试:若预期工作流应包含 '-uid "$uid"' 过滤片段则补回;若为有意移除或重构则同步更新测试期望。仅需恢复工作流内容与断言一致,属最小方向。

  2. 到产生 STATIC_RESULT 的上游静态检查任务日志中定位具体失败项再处理;本批仅能确认门禁按约定拦截,不足以给出针对代码的最小修复建议。

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