Skip to content

perf(hip): compile DFlash GPU draft top-K kernel for HIP#488

Open
cheese-cakee wants to merge 1 commit into
Luce-Org:mainfrom
cheese-cakee:perf/hip-draft-topk
Open

perf(hip): compile DFlash GPU draft top-K kernel for HIP#488
cheese-cakee wants to merge 1 commit into
Luce-Org:mainfrom
cheese-cakee:perf/hip-draft-topk

Conversation

@cheese-cakee

@cheese-cakee cheese-cakee commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Summary

The DFlash GPU draft top-K + log-prob kernel (geometric_draft_topk_cuda.cu, added in #434) was compiled only on the CUDA backend. On HIP/ROCm builds DFLASH27B_HAVE_DRAFT_TOPK_CUDA was never defined, so Qwen35DFlashTarget::project_hidden_to_topk always took the CPU fallback: a full vocab x n_tokens device-to-host logits copy plus a CPU heap extract on every draft step. On a bandwidth-bound card that per-step D2H is a plausible contributor to the gfx1100 "~2x slower than llama.cpp" report in #457.

This compiles the exact same kernel for HIP. The body is pure arithmetic (no tensor cores, no CUDA-only intrinsics), so it builds unchanged through the existing hip_compat shim, the same way flashprefill_kernels.hip.cu is built.

Changes

  • server/src/common/geometric_draft_topk.hip.cu (new): a thin TU that #includes the shared kernel body and is compiled with LANGUAGE HIP + -I hip_compat. On CUDA builds the .cu is still added directly; on HIP only this wrapper is.
  • server/hip_compat/cuda_runtime.h: add the three cuda* -> hip* mappings the kernel needs and the shim did not yet cover (cudaPointerAttributes, cudaPointerGetAttributes, cudaMemoryTypeDevice).
  • Rename the guard macro DFLASH27B_HAVE_DRAFT_TOPK_CUDA -> backend-neutral DFLASH27B_HAVE_DRAFT_TOPK, defined on both backends; update the two call sites (qwen35_dflash_target.cpp, test_dflash.cpp).
  • server/CMakeLists.txt: register test_draft_topk_cuda on the HIP backend too (GPU-vs-CPU parity test), mirroring the existing HIP test_flashprefill_kernels wiring.
  • README.md: correct the note that both flags are CUDA-only.

How it works

DFLASH_GPU_DRAFT_TOPK now takes effect on HIP: the kernel runs top-K + online-logsumexp directly on the logits device buffer and copies back only the n_positions x K results, eliminating the per-step full-vocab D2H and CPU heap extract. Compiling one shared body for both backends keeps the GPU result bit-for-bit identical to the CUDA build and to the CPU reference it is validated against; the automatic CPU fallback on any device failure (out-of-range index, allocation failure, etc.) is unchanged, so the change is output-neutral by construction.

Note on the sibling flag: DFLASH_GPU_VERIFY_ARGMAX already reads an in-graph ggml_argmax node in verify_tree and was never gated on the CUDA-only macro, so it needs no port and already runs on HIP. The README is corrected accordingly.

Performance

Not yet measured. The change removes a vocab x n_tokens (Qwen3.5 vocab ~152k) D2H plus a CPU heap top-K per draft step; the win scales with how bandwidth-bound the card is. A/B recipe on an AMD card: baseline DFLASH_GPU_DRAFT_TOPK=0, treatment DFLASH_GPU_DRAFT_TOPK=1, python server/scripts/bench_llm.py --bench HumanEval (temp 0), comparing decode tok/s and acceptance length.

Limitations

Verification

  • Static: no stale DFLASH27B_HAVE_DRAFT_TOPK_CUDA references remain; guard macro resolves on both backends; new HIP TU and test registration mirror the shipped flashprefill HIP pattern.
  • Not build-validated locally this PR: no GPU toolchain on the authoring machine, and the CUDA validation box was unreachable while preparing this. The CUDA arm change is a macro rename plus a CMake comment, so a clean CUDA rebuild should be a no-op; the HIP arm (compile + the GPU-vs-CPU parity test test_draft_topk_cuda, and DFLASH_GPU_VERIFY_ARGMAX=2 dual-path mismatch mode) needs gfx1100/gfx1151. Requesting the gpu-tests-amd runner build (it compiles the tree) or a maintainer proxy to confirm the HIP compile and parity before merge.

Review in cubic

@cheese-cakee cheese-cakee marked this pull request as draft July 5, 2026 09:48

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 8 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread README.md Outdated
@cheese-cakee cheese-cakee force-pushed the perf/hip-draft-topk branch from 1a0f30a to 40d84e2 Compare July 8, 2026 10:22
@cheese-cakee cheese-cakee marked this pull request as ready for review July 8, 2026 10:23
@cheese-cakee

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (453427d) to resolve the conflicts, and validated the HIP path on real AMD hardware.

On-HW validation (Radeon 8060S, gfx1151, wave32, ROCm 6.4.4): built test_draft_topk_cuda with LANGUAGE HIP (arch gfx1151, Release) and ran it on the GPU. The .hip.cu kernel now actually executes (previously the test skipped for lack of a device), and every case matches the CPU reference:

[PASS] n=15 vocab=151936 K=8 temp=1.00  id_mismatch=0 tie_swap=0 max_lp_err=3.166e-04
[PASS] n=1  vocab=151936 K=8 temp=1.00  id_mismatch=0 tie_swap=0 max_lp_err=1.488e-04
[PASS] n=15 vocab=151936 K=8 temp=0.70  id_mismatch=0 tie_swap=0 max_lp_err=9.727e-05
[PASS] n=15 vocab=151936 K=8 temp=2.00  id_mismatch=0 tie_swap=0 max_lp_err=3.815e-05
[PASS] n=7  vocab=1024   K=8 temp=1.00  id_mismatch=0 tie_swap=0 max_lp_err=9.537e-07
[PASS] n=32 vocab=4096   K=8 temp=1.00  id_mismatch=0 tie_swap=0 max_lp_err=1.335e-05
[PASS] n=3  vocab=257    K=8 temp=1.00  id_mismatch=0 tie_swap=0 max_lp_err=9.537e-07
[PASS] n=1  vocab=151936 K=1 temp=1.00  id_mismatch=0 tie_swap=0 max_lp_err=1.659e-04
[PASS] n=15 vocab=151936 K=4 temp=1.00  id_mismatch=0 tie_swap=0 max_lp_err=2.728e-04
[PASS] fallback contract: K=64 (>kMaxK) returned false
ALL PASS: 10/10 cases

All id_mismatch=0 and max_lp_err <= 3.17e-04 (tolerance 2e-3), including the Qwen3.5 vocab (151936) K=8 decode shape, and the CPU-fallback contract for K beyond the kernel range holds. The change stays output-neutral by construction (automatic CPU fallback on any failure).

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 8 files

Re-trigger cubic

@howard0su howard0su left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggest to cleanup. there are lots of CU files are shared between HIP and CUDA. We should not create hip.cu unless no other approach. This new pattern can confuse people.

Comment thread server/src/common/geometric_draft_topk.hip.cu Outdated
The GPU top-K + log-prob extraction (geometric_draft_topk_cuda.cu) was
compiled only on CUDA, so DFLASH_GPU_DRAFT_TOPK was a no-op on ROCm and
AMD paid a per-step vocab x n_tokens D2H + CPU heap extract every
speculation step. The kernel body is plain arithmetic (no tensor cores,
no CUDA-only intrinsics), so the same .cu compiles unchanged for HIP.

Compile geometric_draft_topk_cuda.cu directly with LANGUAGE HIP through
the hip_compat <cuda_runtime.h> shim (the shared-.cu pattern already used
by deepseek4_hc_cuda.cu), instead of adding a separate .hip.cu wrapper
translation unit. Rename the backend guard macro
DFLASH27B_HAVE_DRAFT_TOPK_CUDA -> DFLASH27B_HAVE_DRAFT_TOPK so the
consumers (qwen35_dflash_target, test_dflash) are backend-neutral.

test_draft_topk_cuda is now built on the HIP backend as well (CUDA
spellings mapped by the same shim) and validated against the CPU
reference on gfx1151 (Radeon 8060S, wave32, ROCm 6.4.4).

Also qualify the README GPU-flag defaults to the server harness.
@cheese-cakee cheese-cakee force-pushed the perf/hip-draft-topk branch from 40d84e2 to 25a123d Compare July 8, 2026 14:30
Comment thread server/CMakeLists.txt
target_compile_definitions(dflash_common PUBLIC DFLASH27B_HAVE_DRAFT_TOPK_CUDA=1)
# and take the GPU draft top-K path instead of the CPU fallback. Same macro
# name as the HIP branch above (backend-neutral).
target_compile_definitions(dflash_common PUBLIC DFLASH27B_HAVE_DRAFT_TOPK=1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shall we have a separate PR to remove this knob as it was default already? DFLASH27B_HAVE_DRAFT_TOPK

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in my opinion better as a separate PR so this one stays scoped to the HIP port

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.

2 participants