Skip to content

Commit f16a48d

Browse files
Add AMD GPU support via ROCm/HIP (#94)
* [ROCm] Add AMD GPU support via ROCm/HIP This adds an optional AMD GPU build to cuPDLPx through ROCm/HIP, alongside the existing CUDA path. The CUDA build is unchanged when USE_HIP is off. To review: start with internal/cuda_to_hip.h, which routes the CUDA runtime, cuBLAS, cuSPARSE, and CUB symbols used by the solver to their hipRT, hipBLAS, hipSPARSE, and hipCUB equivalents on a HIP build, and includes the standard CUDA headers otherwise. The device sources keep their CUDA spelling and are compiled as HIP. internal/cusparse_compat.h selects the standard hipsparseSpMV path on ROCm, since hipSPARSE does not provide the cusparseSpMVOp variant. CMakeLists.txt gains a USE_HIP option (off by default). When enabled the project is configured with the HIP language, the .cu sources are compiled as HIP, and the targets link hipBLAS, hipSPARSE, and hipCUB instead of the CUDA libraries. GPU architectures are chosen with CMAKE_HIP_ARCHITECTURES, defaulting to gfx90a. On Windows the CLI-only mps_parser.c is excluded from the core library because it relies on strtok_r. The interface test gains a case that runs the GPU solver path with presolve disabled, exercising the hipBLAS and hipSPARSE execution path end to end. Test Plan: Built and ran on an AMD Instinct MI200 (gfx90a) with ROCm 7.2.1: ``` cmake -B build -DUSE_HIP=ON -DCMAKE_HIP_ARCHITECTURES=gfx90a -DCMAKE_PREFIX_PATH=/opt/rocm \ -DCUPDLPX_BUILD_CLI=ON -DCUPDLPX_BUILD_TESTS=ON -DCMAKE_BUILD_TYPE=Release cmake --build build -j$(nproc) ./build/tests/test_interface ``` The interface suite passes, including the GPU solver case (Status: OPTIMAL). The same configuration builds cleanly for gfx1100 (RDNA3) and gfx1201 (RDNA4); the device code objects are identical across the documentation and formatting commits that followed validation. The CUDA build path is unaffected by these changes. This work was authored with the assistance of Claude, an AI assistant by Anthropic. * [ROCm] Fix CUDA build: guard cub include for C translation units The ROCm support commit routed all CUDA/HIP includes through internal/cuda_to_hip.h and pulled it into utils.h and internal_types.h, which are included by the C translation units (cli.c, cupdlpx.c, mps_parser.c, presolve.c). On the CUDA path that header included <cub/device/device_reduce.cuh> unconditionally; cub is C++ only, so the C compiler failed with "unknown type name 'namespace'", breaking every CUDA build job (all Linux and Windows toolchains, CUDA 12.4 through 13.1). The HIP path was unaffected because its hipcub include was already guarded with #ifdef __cplusplus. The fix mirrors that guard on the CUDA branch: the cub header is only included for C++ translation units (the .cu device sources that actually use cub::DeviceReduce). The change is entirely within the #else CUDA branch, so the HIP/ROCm device code is unchanged. Authored with assistance from Claude. Test Plan: reproduced and verified the CUDA path locally with the CUDA 12.8 toolkit (gcc 13, ninja), matching the upstream CI configure: ``` cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ -DCUPDLPX_BUILD_TESTS=OFF -DCMAKE_CUDA_ARCHITECTURES=80 cmake --build build --clean-first ``` Before: cc -std=gnu99 -c src/cupdlpx.c fails on cub/device/device_reduce.cuh. After: clean build, links cupdlpx and libcupdlpx.so with 0 errors. * [ROCm] Simplify HIP compat includes and strengthen interface test Addresses review feedback on the ROCm support PR. cuda_to_hip.h already includes the CUDA runtime/cuBLAS/cuSPARSE headers on CUDA builds and their HIP equivalents on ROCm builds, so the per-file `#if !defined(USE_HIP)` include blocks in utils.h and preconditioner.cu were redundant. They are removed in favor of relying on cuda_to_hip.h alone. The USE_HIP definition moves from directory-scoped add_compile_definitions to target_compile_definitions on the cupdlpx_compile_flags interface target so it travels reliably to every consumer, including the Python bindings. test_interface Test 9 previously only checked for a non-NULL result, so it could not catch a wrong answer. It now asserts TERMINATION_REASON_OPTIMAL and the known optimum (objective 3.0 within 1e-4). The default 1e-4 relative tolerance stops the solver around 3.0005, so the test tightens the convergence tolerance to 1e-8, after which the objective reaches the true optimum. On the device-link question: the HIP build does not use relocatable device code (-fgpu-rdc is off), so each object is compiled whole-program and is self-contained; no archive-boundary device link is needed for the static lib. The CUDA path keeps CUDA_SEPARABLE_COMPILATION/CUDA_RESOLVE_DEVICE_SYMBOLS, which it does require. The rationale is now recorded as comments in CMake. Also drops the added per-file copyright/author lines from cuda_to_hip.h to match the project's existing header convention. This work was authored with the assistance of Claude, an AI assistant. Test Plan: Build and run the interface tests on gfx90a (MI250X, ROCm 7.2.1): ``` cmake -S . -B build -DUSE_HIP=ON -DCMAKE_HIP_ARCHITECTURES=gfx90a \ -DCUPDLPX_BUILD_CLI=ON -DCUPDLPX_BUILD_TESTS=ON -DCUPDLPX_BUILD_PYTHON=OFF \ -DCMAKE_BUILD_TYPE=Release cmake --build build -j$(nproc) HIP_VISIBLE_DEVICES=0 ./build/tests/test_interface # 9/9 pass, Test 9 obj=3.000000001 HIP_VISIBLE_DEVICES=0 ./build/cupdlpx 2club200v15p5scn.mps.gz . # OPTIMAL, obj -121.2216698 ``` Build and exercise the ROCm Python extension with a HIP-aware C++ compiler: ``` cmake -S . -B pybuild -DUSE_HIP=ON -DCMAKE_HIP_ARCHITECTURES=gfx90a \ -DCMAKE_CXX_COMPILER=amdclang++ -DCUPDLPX_BUILD_PYTHON=ON -DCMAKE_BUILD_TYPE=Release cmake --build pybuild -j$(nproc) # import + solve -> Status OPTIMAL, ObjVal 3.0, X [1, 2] ``` * apply clang format * CI: add build-hip * [ROCm] Drop remaining duplicate CUDA header includes * Fix missing #endif dropped during moat-port merge conflict resolution The merge commit's manual resolution of internal/cusparse_compat.h dropped the closing #endif // USE_HIP, leaving the outer #if/#else unterminated and breaking the CUDA build. --------- Co-authored-by: ZedongPeng <peng_zedong@126.com>
1 parent 8599563 commit f16a48d

11 files changed

Lines changed: 427 additions & 55 deletions

File tree

.github/workflows/build.yml

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88
workflow_dispatch:
99

1010
jobs:
11-
build:
11+
build-cuda:
1212
strategy:
1313
fail-fast: false
1414
matrix:
@@ -64,3 +64,51 @@ jobs:
6464
shell: pwsh
6565
run: |
6666
cmake --build build --clean-first --config Release
67+
68+
build-hip:
69+
name: build-hip (rocm ${{ matrix.rocm }})
70+
strategy:
71+
fail-fast: false
72+
matrix:
73+
rocm: ["7.2", "7.2.4"]
74+
runs-on: ubuntu-latest
75+
container:
76+
image: rocm/dev-ubuntu-22.04:${{ matrix.rocm }}
77+
env:
78+
DEBIAN_FRONTEND: noninteractive
79+
80+
steps:
81+
# actions/checkout needs git, which the ROCm dev image does not ship with.
82+
- name: Install checkout prerequisites
83+
run: |
84+
apt-get update
85+
apt-get install -y --no-install-recommends git ca-certificates
86+
87+
- uses: actions/checkout@v4
88+
89+
- name: Install build deps
90+
run: |
91+
apt-get install -y --no-install-recommends \
92+
cmake ninja-build zlib1g-dev \
93+
hipblas-dev hipsparse-dev hipcub-dev rocprim-dev
94+
95+
# The ROCm apt packages don't add /opt/rocm/bin to PATH for non-interactive shells.
96+
- name: Add ROCm to PATH
97+
run: echo "/opt/rocm/bin" >> "$GITHUB_PATH"
98+
99+
- name: HIP info
100+
run: |
101+
hipcc --version
102+
103+
- name: Configure
104+
run: |
105+
cmake -B build -G Ninja \
106+
-DCMAKE_BUILD_TYPE=Release \
107+
-DCUPDLPX_BUILD_TESTS=OFF \
108+
-DUSE_HIP=ON \
109+
-DCMAKE_HIP_ARCHITECTURES=gfx90a \
110+
-DCMAKE_PREFIX_PATH=/opt/rocm
111+
112+
- name: Build
113+
run: |
114+
cmake --build build --clean-first

CMakeLists.txt

Lines changed: 113 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,33 @@
33
# -----------------------------------------------------------------------------
44
cmake_minimum_required(VERSION 3.20)
55

6+
# HIP/ROCm support option (must be set before project() to influence language detection)
7+
option(USE_HIP "Build with HIP for AMD GPUs" OFF)
8+
69
# Project config
7-
project(cupdlpx LANGUAGES C CXX)
8-
9-
# Default CUDA architectures: SASS for every current arch, PTX only for the newest one as a forward-compat fallback.
10-
find_package(CUDAToolkit REQUIRED)
11-
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES AND NOT DEFINED ENV{CUDAARCHS})
12-
if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 13)
13-
set(CMAKE_CUDA_ARCHITECTURES 75-real 80-real 86-real 89-real 90-real
14-
100-real 120-real 120-virtual)
15-
elseif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 12.8)
16-
set(CMAKE_CUDA_ARCHITECTURES 60-real 70-real 75-real 80-real 86-real
17-
89-real 90-real 100-real 120-real 120-virtual)
18-
else()
19-
set(CMAKE_CUDA_ARCHITECTURES 60-real 70-real 75-real 80-real 86-real
20-
89-real 90-real 90-virtual)
10+
if(USE_HIP)
11+
project(cupdlpx LANGUAGES C CXX HIP)
12+
else()
13+
project(cupdlpx LANGUAGES C CXX)
14+
15+
# Default CUDA architectures: SASS for every current arch, PTX only for the newest one as a forward-compat fallback.
16+
find_package(CUDAToolkit REQUIRED)
17+
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES AND NOT DEFINED ENV{CUDAARCHS})
18+
if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 13)
19+
set(CMAKE_CUDA_ARCHITECTURES 75-real 80-real 86-real 89-real 90-real
20+
100-real 120-real 120-virtual)
21+
elseif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 12.8)
22+
set(CMAKE_CUDA_ARCHITECTURES 60-real 70-real 75-real 80-real 86-real
23+
89-real 90-real 100-real 120-real 120-virtual)
24+
else()
25+
set(CMAKE_CUDA_ARCHITECTURES 60-real 70-real 75-real 80-real 86-real
26+
89-real 90-real 90-virtual)
27+
endif()
2128
endif()
22-
endif()
2329

24-
enable_language(CUDA)
25-
message(STATUS "CUDA architectures: ${CMAKE_CUDA_ARCHITECTURES}")
30+
enable_language(CUDA)
31+
message(STATUS "CUDA architectures: ${CMAKE_CUDA_ARCHITECTURES}")
32+
endif()
2633

2734
set(CUPDLPX_VERSION_MAJOR 0)
2835
set(CUPDLPX_VERSION_MINOR 2)
@@ -50,6 +57,14 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
5057
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
5158
endif()
5259

60+
if(USE_HIP)
61+
# HIP architecture configuration
62+
# Default to gfx90a if not specified; can override with -DCMAKE_HIP_ARCHITECTURES=gfx1100, etc.
63+
if(NOT DEFINED CMAKE_HIP_ARCHITECTURES OR CMAKE_HIP_ARCHITECTURES STREQUAL "")
64+
set(CMAKE_HIP_ARCHITECTURES "gfx90a")
65+
endif()
66+
endif()
67+
5368
# -----------------------------------------------------------------------------
5469
# [ELEGANT DESIGN] Target-based Compile Flags
5570
# -----------------------------------------------------------------------------
@@ -75,9 +90,14 @@ else()
7590
endif()
7691
endif()
7792

78-
# CUDA standards and RDC
79-
set(CMAKE_CUDA_STANDARD 17)
80-
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
93+
# CUDA/HIP standards and RDC
94+
if(USE_HIP)
95+
set(CMAKE_HIP_STANDARD 17)
96+
set(CMAKE_HIP_STANDARD_REQUIRED ON)
97+
else()
98+
set(CMAKE_CUDA_STANDARD 17)
99+
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
100+
endif()
81101

82102
# -----------------------------------------------------------------------------
83103
# CONTROL OPTIONS
@@ -99,6 +119,14 @@ endif()
99119
# -----------------------------------------------------------------------------
100120
# FIND DEPENDENCIES
101121
# -----------------------------------------------------------------------------
122+
if(USE_HIP)
123+
# Find ROCm/HIP libraries
124+
find_package(hip REQUIRED)
125+
find_package(hipblas REQUIRED)
126+
find_package(hipsparse REQUIRED)
127+
find_package(hipcub REQUIRED)
128+
find_package(rocprim REQUIRED)
129+
endif()
102130
include(FetchContent)
103131

104132
# 1. ZLIB Configuration
@@ -165,20 +193,41 @@ target_compile_definitions(cupdlpx_compile_flags INTERFACE PSLP_VERSION="${PSLP_
165193
file(GLOB C_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.c")
166194
file(GLOB CU_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cu")
167195
list(REMOVE_ITEM C_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/cli.c")
196+
if(WIN32)
197+
# mps_parser.c is CLI-only; exclude it on Windows where strtok_r is unavailable
198+
list(REMOVE_ITEM C_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/mps_parser.c")
199+
endif()
168200

169201
set(CORE_INCLUDE_DIRS
170202
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
171203
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/internal
172204
)
173205

174-
set(CORE_LINK_LIBS
175-
PUBLIC cupdlpx_compile_flags
176-
PUBLIC CUDA::cudart
177-
PUBLIC CUDA::cublas
178-
PUBLIC CUDA::cusparse
179-
PUBLIC ZLIB::ZLIB
180-
PUBLIC PSLP
181-
)
206+
if(USE_HIP)
207+
set(CORE_LINK_LIBS
208+
PUBLIC cupdlpx_compile_flags
209+
PUBLIC hip::device
210+
PUBLIC roc::hipblas
211+
PUBLIC roc::hipsparse
212+
PUBLIC hip::hipcub
213+
PUBLIC ZLIB::ZLIB
214+
PUBLIC PSLP
215+
)
216+
# Mark .cu files as HIP language
217+
set_source_files_properties(${CU_SOURCES} PROPERTIES LANGUAGE HIP)
218+
# Define USE_HIP for the compat header; attach to the interface target so it
219+
# travels to every consumer (core, shared, cli, tests, python bindings).
220+
target_compile_definitions(cupdlpx_compile_flags INTERFACE USE_HIP)
221+
else()
222+
set(CORE_LINK_LIBS
223+
PUBLIC cupdlpx_compile_flags
224+
PUBLIC CUDA::cudart
225+
PUBLIC CUDA::cublas
226+
PUBLIC CUDA::cusparse
227+
PUBLIC ZLIB::ZLIB
228+
PUBLIC PSLP
229+
)
230+
endif()
182231

183232
# 1. Core STATIC Library
184233
if(CUPDLPX_BUILD_STATIC_LIB)
@@ -187,9 +236,23 @@ if(CUPDLPX_BUILD_STATIC_LIB)
187236
target_link_libraries(cupdlpx_core ${CORE_LINK_LIBS})
188237
set_target_properties(cupdlpx_core PROPERTIES
189238
POSITION_INDEPENDENT_CODE ON
190-
CUDA_SEPARABLE_COMPILATION ON
191-
CUDA_RESOLVE_DEVICE_SYMBOLS ON
192239
)
240+
if(USE_HIP)
241+
# HIP compiles device code whole-program by default (-fgpu-rdc off), so
242+
# each object is self-contained and the archive needs no separate device
243+
# link. Host-only consumers (the pybind module) resolve everything at the
244+
# normal link step. HIP_ARCHITECTURES is inherited from CMAKE_HIP_ARCHITECTURES.
245+
set_target_properties(cupdlpx_core PROPERTIES
246+
HIP_ARCHITECTURES "${CMAKE_HIP_ARCHITECTURES}"
247+
)
248+
else()
249+
# CUDA uses relocatable device code here, so device symbols must be
250+
# resolved at the archive boundary for host-only consumers to link.
251+
set_target_properties(cupdlpx_core PROPERTIES
252+
CUDA_SEPARABLE_COMPILATION ON
253+
CUDA_RESOLVE_DEVICE_SYMBOLS ON
254+
)
255+
endif()
193256
endif()
194257

195258
# 2. Shared Library
@@ -200,9 +263,17 @@ if(CUPDLPX_BUILD_SHARED_LIB)
200263
set_target_properties(cupdlpx_shared PROPERTIES
201264
OUTPUT_NAME "cupdlpx"
202265
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
266+
)
267+
if(USE_HIP)
268+
set_target_properties(cupdlpx_shared PROPERTIES
269+
HIP_ARCHITECTURES "${CMAKE_HIP_ARCHITECTURES}"
270+
)
271+
else()
272+
set_target_properties(cupdlpx_shared PROPERTIES
203273
CUDA_SEPARABLE_COMPILATION ON
204274
CUDA_RESOLVE_DEVICE_SYMBOLS ON
205-
)
275+
)
276+
endif()
206277
endif()
207278

208279
# 3. CLI Executable
@@ -217,8 +288,12 @@ if(CUPDLPX_BUILD_CLI)
217288
set_target_properties(cupdlpx_cli PROPERTIES
218289
OUTPUT_NAME "cupdlpx"
219290
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
220-
CUDA_RESOLVE_DEVICE_SYMBOLS ON
221291
)
292+
if(NOT USE_HIP)
293+
set_target_properties(cupdlpx_cli PROPERTIES
294+
CUDA_RESOLVE_DEVICE_SYMBOLS ON
295+
)
296+
endif()
222297
endif()
223298

224299
# 4. Tests
@@ -230,14 +305,18 @@ if(CUPDLPX_BUILD_TESTS)
230305
enable_testing()
231306
file(GLOB TEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/test/*.c" "${CMAKE_CURRENT_SOURCE_DIR}/test/*.cu")
232307
foreach(TEST_SRC ${TEST_SOURCES})
233-
get_filename_component(TEST_NAME ${TEST_SRC} NAME_WE)
308+
get_filename_component(TEST_NAME ${TEST_SRC} NAME_WE)
234309
add_executable(${TEST_NAME} ${TEST_SRC})
235310
target_link_libraries(${TEST_NAME} PRIVATE cupdlpx_core)
236311
target_include_directories(${TEST_NAME} PRIVATE include internal)
237312
set_target_properties(${TEST_NAME} PROPERTIES
238313
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/tests"
239-
CUDA_RESOLVE_DEVICE_SYMBOLS ON
240314
)
315+
if(NOT USE_HIP)
316+
set_target_properties(${TEST_NAME} PROPERTIES
317+
CUDA_RESOLVE_DEVICE_SYMBOLS ON
318+
)
319+
endif()
241320
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
242321
endforeach()
243322
endif()

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ Our work is presented in two papers:
2626
## Installation
2727

2828
### Requirements
29-
* **GPU:** NVIDIA GPU with CUDA 12.4+.
30-
* **Build Tools:** CMake (≥ 3.20), GCC, NVCC.
29+
* **GPU:** NVIDIA GPU with CUDA 12.4+, or AMD GPU with ROCm 7.2+.
30+
* **Build Tools:** CMake (≥ 3.20), GCC, and NVCC (CUDA) or hipcc (ROCm).
3131

3232
> **SpMV backend** is selected automatically at compile time based on cuSPARSE version:
3333
> - `cusparseSpMV` — CUDA 12.4 – 13.2 (cuSPARSE < 12.8.1)
3434
> - `cusparseSpMVOp` — CUDA 13.3+ (cuSPARSE ≥ 12.8.1)
35+
>
36+
> On AMD GPUs the solver uses the `hipsparseSpMV` backend via hipSPARSE.
3537
3638
### Build from Source
3739
Clone the repository and compile the project using CMake.
@@ -43,6 +45,21 @@ cmake --build build --clean-first
4345
```
4446
This will create the solver binary at `./build/cupdlpx`.
4547

48+
#### Building for AMD GPUs (ROCm/HIP)
49+
To target AMD GPUs, configure with `-DUSE_HIP=ON` and select the GPU
50+
architecture with `-DCMAKE_HIP_ARCHITECTURES`. The CUDA sources are compiled
51+
as HIP and the cuBLAS/cuSPARSE/CUB calls are mapped to hipBLAS/hipSPARSE/hipCUB.
52+
```bash
53+
cmake -B build -DUSE_HIP=ON -DCMAKE_HIP_ARCHITECTURES=gfx90a -DCMAKE_PREFIX_PATH=/opt/rocm
54+
cmake --build build --clean-first
55+
```
56+
Set `CMAKE_HIP_ARCHITECTURES` to match your GPU (for example `gfx90a` for
57+
MI200, `gfx1100` for RDNA3 desktop, or `gfx1201` for RDNA4). If the ROCm
58+
install is not on CMake's default search path, point `-DCMAKE_PREFIX_PATH` at
59+
it (e.g. `/opt/rocm`) so `find_package` can locate hip, hipBLAS, hipSPARSE,
60+
and hipCUB. The resulting `./build/cupdlpx` binary is used exactly as in the
61+
CUDA build.
62+
4663
#### Verifying the Installation
4764
Run a small test problem to confirm that the solver was built correctly.
4865
```bash

0 commit comments

Comments
 (0)