Skip to content

[DeepSeek-V4] Emit fp32 directly in linear_bf16_fp32 (drop .float() copy) - #28877

Closed
AMD-yanfeiwang wants to merge 1 commit into
sgl-project:mainfrom
AMD-yanfeiwang:fix/dsv4-linear-bf16-fp32-native
Closed

AMD-yanfeiwang wants to merge 1 commit into
sgl-project:mainfrom
AMD-yanfeiwang:fix/dsv4-linear-bf16-fp32-native

Conversation

@AMD-yanfeiwang

@AMD-yanfeiwang AMD-yanfeiwang commented Jun 22, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

In the DSV4 indexer kv_score path, linear_bf16_fp32 (AITER branch) computes a bf16 GEMM and then casts to fp32 with .float():

return tgemm.mm(x, y, otype=x.dtype).float()

Profiling DeepSeek-V4 (8xMI355, tp=8) shows this .float() as a separate bfloat16tofloat32_copy kernel launched once per call (per layer, per step), e.g. output shapes [256, {512,1024,2048}], launched from compute_kv_score -> linear_bf16_fp32. It is small individually but recurs every decode step, and computing in bf16 then up-casting also loses precision.

Modifications

  • Request fp32 output directly from the GEMM (otype=torch.float32) so the up-cast is fused into the epilogue and the separate copy kernel is removed.

Dependency

This is the consumer-side half. To actually avoid the copy (rather than relocate it), AITER must expose a native fp32-output tuned kernel for these bf16xbf16 shapes — companion: ROCm/aiter#3836. Without it, tgemm falls back internally (correctness unaffected, but the copy/torch-fallback may remain), which is why this is kept as a draft until the AITER configs land.

Accuracy / Speed

  • Functionally identical math; emitting fp32 directly is strictly more accurate than bf16-GEMM + up-cast (avoids intermediate bf16 rounding).
  • Removes one bfloat16tofloat32_copy kernel per linear_bf16_fp32 call.

Checklist


CI States

Latest PR Test (Base): ❌ Run #27928226891
Latest PR Test (Extra): ❌ Run #27928226844

…at() copy

The AITER path of linear_bf16_fp32 computed a bf16 GEMM and then cast the
result to fp32 with `.float()`. That cast shows up in profiles as a separate
`bfloat16tofloat32_copy` kernel launched once per call in the DSV4 indexer
kv_score path. Request fp32 output from the GEMM directly so the up-cast is
fused into the epilogue, removing the copy and avoiding rounding the result
to bf16 first.

Pairs with an AITER change that provides a native fp32-output kernel for these
bf16xbf16 shapes; without it tgemm falls back internally (no regression in
correctness).
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@github-actions

Copy link
Copy Markdown
Contributor

Thanks @AMD-yanfeiwang. Closing this because it is still a draft and has not been updated in 87 days.

Reopen it if the work is still relevant.

Some directories moved recently, so an older branch may need retargeting:
sgl-kernel/ -> python/sglang/kernels/aot/, python/sglang/jit_kernel/
-> python/sglang/kernels/jit/, docs/ -> docs/docs/ (.mdx),
bench_serving.py -> benchmark/serving.py, test/srt/ -> test/registered/.

@github-actions github-actions Bot closed this Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant