[ROCm] Add native HIP backend for AMD GPUs#320
Conversation
Adds a native HIP backend (DEVICE=HIP) to AutoDock-GPU by porting the maintained CUDA kernels (cuda/) to HIP: a new Makefile.Hip builds cuda/kernels.cu with hipcc, and a new compat header cuda/cuda_to_hip.h aliases the CUDA spellings to HIP so the CUDA-spelled device sources compile unchanged. AMD users get the same kernels NVIDIA users get (the existing OpenCL backend is a separate code path and is in fact broken on ROCm 7.2). The CUDA and OpenCL/CPU build paths are byte-for-byte unchanged. The offload arch is configurable (HIP_ARCH ?= gfx90a), so other AMD targets build with HIP_ARCH=<arch> and no source edit. AMD's wavefront is 64 lanes on gfx90a, so the warp reductions are adapted natively: the host derives warpmask/warpbits from the runtime warpSize (31/5 on NVIDIA, 63/6 on gfx90a) and the device reduction macros gain a stride-32 step under a 64-lane wavefront (a 6-step XOR-butterfly all-reduce; CUDA keeps the upstream 5-step). Per-warp partials recombine block-wide via atomicAdd, so the native-64 reduction is the correct choice rather than splitting into two 32-lane halves. __shfl_sync/__any_sync use a 64-bit full mask on HIP (ROCm rejects the 32-bit literal), and the two inline-PTX mov.b64 helpers are replaced by an equivalent memcpy bit-cast. On Windows, the HIP device pass defines both _WIN32 and __HIP_DEVICE_COMPILE__, but the Windows SDK header processthreadsapi.h requires a host-architecture macro (_AMD64_/_X86_) that the device pass does not set, so winnt.h emits a hard "No Target Architecture" error. Guard the processthreadsapi.h include with !__HIP_DEVICE_COMPILE__ (host compilation only) and add a stub processid() for the device-compile path, since LocalRNG and Dockpars default members reference it in header-inlined code the device pass sees. The ROCm/HIP build is documented in the README alongside the CUDA build. The optional TENSOR=ON build (USE_NVTENSOR) is supported on AMD as well: it accelerates the energy/gradient sum-reduction with the GPU matrix cores through rocWMMA, mirroring the CUDA tensor-core path and requiring NUMWI >= 64. On CDNA (gfx90a) this uses the native fp32 WMMA; on RDNA, which has no fp32 WMMA, it is emulated with a bf16 split. Results match the standard reduction. Test Plan: ``` cd src && make DEVICE=HIP NUMWI=64 HIP_ARCH=gfx90a # default reduction cd src && make DEVICE=HIP NUMWI=64 HIP_ARCH=gfx90a TENSOR=ON # rocWMMA reduction ``` Validated by docking the bundled streptavidin-biotin case (input/1stp, -nrun 10, fixed seeds, each on a single pinned GPU) on three AMD architectures, all recovering the native biotin pose (all 10 runs converging into one cluster): - gfx90a (CDNA2, MI250X, ROCm 7.2.1): -8.28 kcal/mol, reference RMSD 0.48 A (64wi, seed 42); seed 7 -8.37 / 0.37 A; NUMWI=128 -8.33 / 0.39 A. - gfx1100 (RDNA3, Radeon Pro W7800, ROCm 7.2.1): -8.28 / 0.40 A (64wi, seed 42); seed 7 -8.37 / 0.38 A; NUMWI=128 -8.35 / 0.41 A. - gfx1201 (RDNA4, Radeon RX 9070 XT, ROCm 7.14): -8.28 / 0.59 A (64wi, seed 42); seed 7 -8.37 / 0.39 A; NUMWI=128 -8.28 / 0.59 A. Best energies agree within 0.05 kcal/mol across architectures (sub-0.6 A reference RMSD throughout), and the TENSOR=ON (rocWMMA) build produces the same poses and energies as the default reduction. This confirms the GA min-reduction (kernel4) and the energy/gradient sum-reductions (calcenergy/calcMergeEneGra) are numerically correct on both wave64 (gfx90a) and wave32 (RDNA), including the NUMWI=128 cross-warp final reduction. Authored with the assistance of Claude, an AI assistant.
Testing report: HIP backend on RDNA4 (gfx1201 / RX 9070 XT)Gave this branch a spin on a consumer AMD card — nice to confirm the arch-configurable design works Setup: CorrectnessAll work-group sizes converge to the same energy on the bundled inputs — e.g. NUMWI sweep — best-of-N end-to-end job-set time (relative to NUMWI=64; higher % = slower)
Absolute anchor for cross-GPU comparison: Kernel profiling (rocprofv3
|
| Kernel | Calls | GPU time | % |
|---|---|---|---|
gpu_gradient_minAD_kernel |
144 | 7.54 s | 99.6% |
gpu_gen_and_eval_newpops_kernel |
144 | 27.5 ms | 0.36% |
gpu_sum_evals_kernel |
145 | 0.49 ms | <0.01% |
gpu_calc_initpop_kernel |
1 | 0.23 ms | <0.01% |
Resource usage for the hot kernel: 152 VGPR, 0 scratch spill, 7680 B LDS/block. That caps
occupancy at 9 of 16 waves/SIMD — it's VGPR-bound, not LDS-bound. This also explains the
sweep: at NUMWI=256 (8 waves/block) occupancy collapses, hence the ~55–64% regression.
VGPR-reduction attempt (no luck, reporting for completeness)
Tried to trade registers for occupancy via __attribute__((amdgpu_waves_per_eu(10/12/16))) on the
hot kernel (this LLVM has no -mllvm -amdgpu-waves-per-eu global flag). VGPR stayed pinned at 152
with zero spill and no runtime change — the existing
__launch_bounds__(NUM_OF_THREADS_PER_BLOCK, 1024 / NUM_OF_THREADS_PER_BLOCK) already requests max
occupancy, and the allocator correctly declines to go lower (spilling would be net-negative). So
152 VGPR looks intrinsic to the kernel's live-set; further gains would need source-level changes
(recompute vs. store, narrower types), not compiler flags.
Minor note (pre-existing, unrelated to this PR)
input/7cpa doesn't run out of the box: its .maps.fld references rec.A.map (etc.) but the map
files are named 7cpa_rec.A.map. I symlinked around it to test. Might be worth fixing separately.
Bottom line: HIP backend works great on RDNA4 / gfx1201 — correct results, clean build,
sensible perf. Suggest documenting NUMWI=64 as the default guidance for consumer AMD cards. 👍
An independent validation on a consumer RDNA GPU (gfx1201) swept the work-group size and found NUMWI=64 fastest, with 32/128/256 all slower. That matches the measurements from this port on a datacenter card, where NUMWI=64 also gave the best docking throughput. Add a one-line hint to the build documentation so AMD users start from the fast setting. Documentation only; no code or build behavior changes. Authored with assistance from Claude. Test Plan: # Documentation-only change; existing DEVICE=HIP build unaffected. make DEVICE=HIP NUMWI=64 HIP_ARCH=gfx90a
|
Thanks for testing this on RDNA4 and profiling the hot kernel. The NUMWI=64 guidance is now in the build docs (2966dcd); it was also the fastest setting in our measurements on the datacenter default, so the recommendation holds across both. The VGPR-bound profile for gpu_gradient_minAD_kernel matches what we saw here: the 152-VGPR live set is intrinsic to the kernel, so occupancy gains would need source-level changes rather than compiler flags. On 7cpa, the .maps.fld referencing rec.A.map while the shipped maps are named 7cpa_rec.A.map is a pre-existing issue in the input data (this PR does not touch input/), so it is unrelated here. |
Summary
This adds a native HIP backend (
DEVICE=HIP) so AutoDock-GPU runs on AMD GPUs through ROCm, by porting the maintained CUDA kernels (cuda/) to HIP. A newMakefile.Hipbuildscuda/kernels.cuwithhipcc, and a new compat headercuda/cuda_to_hip.haliases the CUDA spellings to HIP so the CUDA-spelled device sources compile unchanged. AMD users get the same kernels NVIDIA users get; the existing OpenCL backend is a separate code path. The CUDA and OpenCL/CPU build paths are byte-for-byte unchanged.Build it with
make DEVICE=HIP NUMWI=<N> HIP_ARCH=<arch>. The offload arch is configurable (HIP_ARCH ?= gfx90a), so other AMD targets build withHIP_ARCH=<arch>and no source edit. The build is documented in the README alongsideDEVICE=CUDA.What is implemented
Makefile.Hip+cuda/cuda_to_hip.h: a HIP build of the CUDA kernels via a single compat header (the CUDAcuda/kernels are reused as-is).warpmask/warpbitsfrom the runtimewarpSize(31/5 on NVIDIA, 63/6 on gfx90a) and the device reduction macros gain a stride-32 step under a 64-lane wavefront (a 6-step XOR-butterfly all-reduce; CUDA keeps the upstream 5-step). Per-warp partials recombine block-wide viaatomicAdd.__shfl_sync/__any_syncuse a 64-bit full mask, and two inline-PTXmov.b64helpers become an equivalentmemcpybit-cast.TENSOR=ON(rocWMMA): the optional tensor-core build (USE_NVTENSOR,NUMWI >= 64) accelerates the energy/gradient sum-reduction with the GPU matrix cores through rocWMMA, mirroring the CUDA tensor-core path. CDNA (gfx90a) uses the native fp32 WMMA; RDNA, which has no fp32 WMMA, uses a bf16-split emulation. Results match the standard reduction._WIN32and__HIP_DEVICE_COMPILE__but not a host-architecture macro, so the Windows SDKprocessthreadsapi.herrors with "No Target Architecture". It is guarded with!__HIP_DEVICE_COMPILE__(host compilation only), with a device-side stubprocessid()for header-inlined references.Validation
The bundled streptavidin-biotin case (
input/1stp) was docked with-nrun 10and fixed seeds on a single pinned GPU, for both the default andTENSOR=ONbuilds, recovering the native biotin pose (all 10 runs in one cluster):Results reproduce across seeds (seed 7: -8.37 kcal/mol on each) and block sizes (
NUMWI=128, which also exercises the cross-warp final reduction); best energies agree within 0.05 kcal/mol across architectures and reference RMSD stays sub-0.6 A. TheTENSOR=ON(rocWMMA) build produces the same poses and energies as the default reduction. This confirms the GA min-reduction (kernel4) and the energy/gradient sum-reductions are numerically correct on both wave64 and wave32.The CUDA/OpenCL/CPU paths are unchanged and not affected by this backend.
This work was authored with the assistance of Claude, an AI assistant.