Repository navigation
[ROCm] Preserve float bits in SIMT DPP reduction - #2089
liminfei-amd wants to merge 2 commits into
Conversation
The AMD mov_dpp builtin operates on integer bits. Explicitly bitcast the floating-point accumulator before and after each lane move so older ROCm compilers do not emit numeric float-to-integer conversions. Add a gfx11 regression test covering the fused Linear4bit path for fp16, bf16, and fp32. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Hi liminfei-amd, I also confirmed that it stands on the codegen side for all targets under ROCm 6.4.4. Compiling just those 4 DPP steps with clang for the amdgcn target generates a pair of cvt instructions in the unmodified version. With the fix it generates 1 instruction per DPP. Details below. It appears the issue is clang version specific. With upstream clang 19.1.7 it will generate v_cvt_i32_f32 and v_cvt_f32_i32_dpp instructions on the unmodified code. So it converts the partial sums into integers before the shift, thus losing precision. With the modification it will generate a single instruction per DPP, v_add_f32_dpp, which is what we want. With later versions of upstream clang it does not generate integer conversion on the unmodified code. Specifically, clang 20.1.8, clang 21.1.8, clang 22.1.8, no int conversion on the old code. clang 23.1.2 generates v_add_f32_dpp with or without the bitcast. So this seems to be a special issue with clang19, the version used by ROCm 6.4.4. Also it happens on all 9 targets I've compiled, which are from the ROCm 6.4.4 list, gfx1100, gfx1101, gfx1102, gfx1150, gfx1151, gfx1200, gfx1201, gfx90a, gfx942. Modeling lane split and reduction on CPU using the repro shape, M 8, N 256 and K 512, confirms converting the moved value will result in 0.1245 relative L1 error against the dequantized weight on bf16 and 0.1246 on fp16 and fp32, which is very close to 0.125295 as you've seen on GRE. Keeping the bits will result in 0.0 error. Suggestions I noticed _gemm_4bit_use_custom_rocm allows the use of this kernel on gfx12 for M up to 8, as well as gfx9 for M up to 4 on fp16 and bf16 or M up to 2 on fp32. So perhaps the newly added test should be enabled for all archs that invoke this kernel? For example simply check on _gemm_4bit_use_custom_rocm, and also include M 1 and 2 in the list. That way it will be tested by RDNA4 and MI300 runners. Some comments are wrong. For example all those constants, 0x101 to 0x10f, are row_shl, row_shl:8 in the printout, but the comments call them row_shr. The instructions are correct for lane 0 though. I created a branch that makes the same modification, fixes comments and extends test. Take any of it into this PR. |
|
Branch is up at https://github.com/LaelaZorana/bitsandbytes/tree/fix/rocm-dpp-float-bits-2087 and the diff against main is at main...LaelaZorana:bitsandbytes:fix/rocm-dpp-float-bits-2087 |
|
To be exact about the compiler, the matrix above used upstream clang 19.1.7, the same major version ROCm 6.4.4 ships, not the ROCm toolchain itself. The 0.0 is the CPU model checked against its own dequantized reference, and the 0.002051 you measured on the GRE is the real hardware number. |
mov_dpp takes an int before clang 20, so the ROCm 6.4.4 build converted each partial sum to an integer and back. Pass the bits instead, fix the row_shl labels, and test the fused path on every ROCm arch that selects it. [liminfei-amd: applied on top of bitsandbytes-foundation#2089, which already passes the float bits; this commit carries the comment, row_shl label, and test changes.] (cherry picked from commit 0b5a20e)
|
Thanks @LaelaZorana, this was very helpful. Your commit is now part of this PR with your authorship preserved (eb5f933). It brings in the I ran the new test on RX 7900 GRE (gfx1100):
|
Summary
__builtin_amdgcn_mov_dppin the HIP SIMT warp reduction.0x101-0x10farerow_shl, notrow_shr.Linear4bitpath. It runs on every ROCmarchitecture where
_gemm_4bit_use_custom_rocmselects the fused SIMTkernel, for M = 1, 2, and 8 and fp16, bf16, and fp32, and compares against an
fp32 matmul with the same dequantized weight.
The second commit is from @LaelaZorana, with her authorship preserved. It adds
the comment correction and the broader test.
Fixes #2087.
Root cause
The HIP warp reduction passed a
floatdirectly to__builtin_amdgcn_mov_dpp. In clang 19, which ROCm 6.4.x uses, the builtintakes an
int, so each partial sum was converted to an integer and back(
v_cvt_i32_f32+v_cvt_f32_i32_dpp) instead of moving its IEEE-754 bits.LLVM 20 made the builtin overloaded and removed the implicit conversion
(llvm/llvm-project#112447), so newer compilers generate correct code for the
old source.
Explicit
__float_as_uint/__uint_as_floatconversions move the bits withevery compiler. ROCm 6.4.4 is still in the bitsandbytes build matrix.
Validation
The nine new test cases (fp16, bf16, fp32 x M = 1, 2, 8):
cuda:0andcuda:1)change above, so the test catches this bug only in clang 19 builds.
clang 19 code objects were checked instead: without the fix, the 24 SIMT
kernels contain 96
v_cvt_f32_i32_dppinstructions; with the fix theycontain none and the reduction uses
v_add_f32_dpp.pre-commit run --all-filespassed.RDNA4 and CDNA hardware were not tested. The model workload from the original
report was not rerun; the test uses the minimal tensor reproducer.
AI assistance was used for reproduction, diagnosis, validation, and drafting
this description. I reviewed the changes and the validation results.