Loading...
Searching...
No Matches
hip.cpp
Go to the documentation of this file.
1/**
2 * HIPGPUFFTGrid — GPUFFTGrid implementation using AMD hipFFT.
3 *
4 * Provides both FP64 (hipfftExecZ2Z) and FP32 (hipfftExecC2C) complex-to-complex
5 * FFT transforms. hipFFT plans are created during init() and destroyed on
6 * destruction.
7 *
8 * For the base FFTGrid interface (grid dimensions, g-vector mapping, buffers),
9 * this class composes a SerialFFTGrid internally and delegates all base-class
10 * pure virtual calls to it. This avoids diamond inheritance while reusing
11 * the complete FFTW-based implementation from SerialFFTGrid.
12 *
13 * hipFFT operates on device pointers. With unified memory (hipMallocManaged),
14 * the same pointers are accessible from both host and device.
15 */
16
19
20#include <hip/hip_runtime.h>
21#include <hipfft/hipfft.h>
22#include <stdexcept>
23#include <string>
24
25namespace q3 {
26namespace gpu {
27
28namespace {
29
30/**
31 * Check a hipFFT return code and throw on error.
32 */
33void check_hipfft_error(hipfftResult result, const char* operation) {
34 if (result != HIPFFT_SUCCESS) {
35 throw std::runtime_error(std::string("HIPGPUFFTGrid: ") + operation +
36 " failed with result " + std::to_string(static_cast<int>(result)));
37 }
38}
39
40} // anonymous namespace
41
42class HIPGPUFFTGrid : public GPUFFTGrid {
43 public:
44 /**
45 * Construct a HIP FFT grid backed by the given device.
46 *
47 * hipFFT plans are not created here — they are created in init() after
48 * the grid dimensions are known.
49 */
50 explicit HIPGPUFFTGrid(std::shared_ptr<execution::gpu::device::GPUDeviceBackend> device_backend)
51 : GPUFFTGrid(std::move(device_backend)) {}
52
53 ~HIPGPUFFTGrid() override {
54 if (plan_fp64_ != 0) {
55 hipfftDestroy(plan_fp64_);
56 }
57 if (plan_fp32_ != 0) {
58 hipfftDestroy(plan_fp32_);
59 }
60 if (device_buffer_fp32_ != nullptr) {
61 hipFree(device_buffer_fp32_);
62 }
63 if (device_buffer_fp64_ != nullptr) {
64 hipFree(device_buffer_fp64_);
65 }
66 if (plan_fp32_batch_ != 0) {
67 hipfftDestroy(plan_fp32_batch_);
68 }
69 if (device_buffer_fp32_batch_ != nullptr) {
70 hipFree(device_buffer_fp32_batch_);
71 }
72 if (plan_fp64_batch_ != 0) {
73 hipfftDestroy(plan_fp64_batch_);
74 }
75 if (device_buffer_fp64_batch_ != nullptr) {
76 hipFree(device_buffer_fp64_batch_);
77 }
78 }
79
80 // ── FFTGrid base interface (delegated to serial_fft_grid_) ───────────
81
82 void init(int dimension_1,
83 int dimension_2,
84 int dimension_3,
85 bool use_measured_planning = false) override {
86 // Initialize the serial FFT grid (sets up dimensions and FFTW plans).
87 serial_fft_grid_.init(dimension_1, dimension_2, dimension_3, use_measured_planning);
88
89 // Create hipFFT FP64 plan (Z2Z = complex double-to-complex double).
90 check_hipfft_error(
91 hipfftPlan3d(&plan_fp64_, dimension_1, dimension_2, dimension_3, HIPFFT_Z2Z),
92 "hipfftPlan3d FP64");
93
94 // Create hipFFT FP32 plan (C2C = complex float-to-complex float).
95 check_hipfft_error(
96 hipfftPlan3d(&plan_fp32_, dimension_1, dimension_2, dimension_3, HIPFFT_C2C),
97 "hipfftPlan3d FP32");
98
99 // Allocate persistent device buffers for hipFFT.
100 total_grid_points_cached_ = serial_fft_grid_.total_grid_points();
101
102 const size_t buffer_size_fp32 =
103 static_cast<size_t>(total_grid_points_cached_) * sizeof(hipfftComplex);
104 hipMalloc(&device_buffer_fp32_, buffer_size_fp32);
105
106 const size_t buffer_size_fp64 =
107 static_cast<size_t>(total_grid_points_cached_) * sizeof(hipfftDoubleComplex);
108 hipMalloc(&device_buffer_fp64_, buffer_size_fp64);
109 }
110
111 [[nodiscard]] int grid_dimension_1() const override {
112 return serial_fft_grid_.grid_dimension_1();
113 }
114 [[nodiscard]] int grid_dimension_2() const override {
115 return serial_fft_grid_.grid_dimension_2();
116 }
117 [[nodiscard]] int grid_dimension_3() const override {
118 return serial_fft_grid_.grid_dimension_3();
119 }
120 [[nodiscard]] int total_grid_points() const override {
121 return serial_fft_grid_.total_grid_points();
122 }
123 [[nodiscard]] int local_grid_points() const override {
124 return serial_fft_grid_.local_grid_points();
125 }
126 [[nodiscard]] int local_offset() const override {
127 return serial_fft_grid_.local_offset();
128 }
129
130 void forward(Complex* data) const override {
131 serial_fft_grid_.forward(data);
132 }
133 void inverse(Complex* data) const override {
134 serial_fft_grid_.inverse(data);
135 }
136 void inverse_raw(Complex* data) const override {
137 serial_fft_grid_.inverse_raw(data);
138 }
139
140 [[nodiscard]] int
141 g_vector_to_fft_grid_index(int miller_1, int miller_2, int miller_3) const override {
142 return serial_fft_grid_.g_vector_to_fft_grid_index(miller_1, miller_2, miller_3);
143 }
144
145 void reduce_g_space(Complex* data, int number_of_elements) const override {
146 serial_fft_grid_.reduce_g_space(data, number_of_elements);
147 }
148
149 Complex* input_buffer() const override {
150 return serial_fft_grid_.input_buffer();
151 }
152 Complex* output_buffer() const override {
153 return serial_fft_grid_.output_buffer();
154 }
155
156 void execute_inverse_raw_on_buffers() const override {
157 serial_fft_grid_.execute_inverse_raw_on_buffers();
158 }
159 void execute_forward_on_buffers() const override {
160 serial_fft_grid_.execute_forward_on_buffers();
161 }
162 void prepare_forward_from_output() const override {
163 serial_fft_grid_.prepare_forward_from_output();
164 }
165
166 // ── FP32 FFT (hipFFT via persistent device buffer) ──────────────────────
167
168 /**
169 * Forward FP32 FFT via hipfftExecC2C with HIPFFT_FORWARD.
170 *
171 * Uses a persistent device buffer allocated in init() to avoid
172 * per-call hipMalloc/hipFree overhead.
173 */
174 void forward_single(ComplexSingle* data) const override {
175 const size_t buffer_size =
176 static_cast<size_t>(total_grid_points_cached_) * sizeof(hipfftComplex);
177 hipMemcpy(device_buffer_fp32_, data, buffer_size, hipMemcpyDefault);
178
179 check_hipfft_error(
180 hipfftExecC2C(plan_fp32_, device_buffer_fp32_, device_buffer_fp32_, HIPFFT_FORWARD),
181 "hipfftExecC2C forward");
182
183 hipMemcpy(data, device_buffer_fp32_, buffer_size, hipMemcpyDefault);
184 }
185
186 /**
187 * Inverse FP32 FFT (raw, unnormalized) via hipfftExecC2C with HIPFFT_BACKWARD.
188 *
189 * Uses a persistent device buffer allocated in init() to avoid
190 * per-call hipMalloc/hipFree overhead.
191 */
192 void inverse_raw_single(ComplexSingle* data) const override {
193 const size_t buffer_size =
194 static_cast<size_t>(total_grid_points_cached_) * sizeof(hipfftComplex);
195 hipMemcpy(device_buffer_fp32_, data, buffer_size, hipMemcpyDefault);
196
197 check_hipfft_error(
198 hipfftExecC2C(plan_fp32_, device_buffer_fp32_, device_buffer_fp32_, HIPFFT_BACKWARD),
199 "hipfftExecC2C inverse");
200
201 hipMemcpy(data, device_buffer_fp32_, buffer_size, hipMemcpyDefault);
202 }
203
204 // ── FP64 FFT (hipFFT via persistent device buffer) ──────────────────────
205
206 /**
207 * Forward FP64 FFT via hipfftExecZ2Z with HIPFFT_FORWARD.
208 *
209 * Uses a persistent device buffer allocated in init() to avoid
210 * per-call hipMalloc/hipFree overhead.
211 */
212 void forward_transform(Complex* data) const override {
213 const size_t buffer_size =
214 static_cast<size_t>(total_grid_points_cached_) * sizeof(hipfftDoubleComplex);
215 hipMemcpy(device_buffer_fp64_, data, buffer_size, hipMemcpyDefault);
216
217 check_hipfft_error(
218 hipfftExecZ2Z(plan_fp64_, device_buffer_fp64_, device_buffer_fp64_, HIPFFT_FORWARD),
219 "hipfftExecZ2Z forward");
220
221 hipMemcpy(data, device_buffer_fp64_, buffer_size, hipMemcpyDefault);
222 }
223
224 /**
225 * Inverse FP64 FFT (raw, unnormalized) via hipfftExecZ2Z with HIPFFT_BACKWARD.
226 *
227 * Uses a persistent device buffer allocated in init() to avoid
228 * per-call hipMalloc/hipFree overhead.
229 */
230 void inverse_transform(Complex* data) const override {
231 const size_t buffer_size =
232 static_cast<size_t>(total_grid_points_cached_) * sizeof(hipfftDoubleComplex);
233 hipMemcpy(device_buffer_fp64_, data, buffer_size, hipMemcpyDefault);
234
235 check_hipfft_error(
236 hipfftExecZ2Z(plan_fp64_, device_buffer_fp64_, device_buffer_fp64_, HIPFFT_BACKWARD),
237 "hipfftExecZ2Z inverse");
238
239 hipMemcpy(data, device_buffer_fp64_, buffer_size, hipMemcpyDefault);
240 }
241
242 /**
243 * ── Batched FP32 FFT (hipfftPlanMany) ─────────────────────────────────
244 *
245 * Transforms multiple contiguous FFT grids in a single kernel launch.
246 * The batch plan and device buffer are created lazily on first use and
247 * resized if the batch size grows.
248 */
249
250 /**
251 * Batched forward FP32 FFT via hipfftPlanMany + hipfftExecC2C.
252 *
253 * All batch_size grids are transformed in a single hipFFT kernel launch,
254 * eliminating per-band dispatch overhead.
255 */
256 void forward_single_batch(ComplexSingle* data, int batch_size) const override {
257 ensure_batch_resources(batch_size);
258
259 const size_t buffer_size =
260 static_cast<size_t>(total_grid_points_cached_) * batch_size * sizeof(hipfftComplex);
261 hipMemcpy(device_buffer_fp32_batch_, data, buffer_size, hipMemcpyDefault);
262
263 check_hipfft_error(hipfftExecC2C(plan_fp32_batch_,
264 device_buffer_fp32_batch_,
265 device_buffer_fp32_batch_,
266 HIPFFT_FORWARD),
267 "hipfftExecC2C batched forward");
268
269 hipMemcpy(data, device_buffer_fp32_batch_, buffer_size, hipMemcpyDefault);
270 }
271
272 /**
273 * Batched inverse FP32 FFT (raw, unnormalized) via hipfftPlanMany + hipfftExecC2C.
274 *
275 * All batch_size grids are transformed in a single hipFFT kernel launch,
276 * eliminating per-band dispatch overhead.
277 */
278 void inverse_raw_single_batch(ComplexSingle* data, int batch_size) const override {
279 ensure_batch_resources(batch_size);
280
281 const size_t buffer_size =
282 static_cast<size_t>(total_grid_points_cached_) * batch_size * sizeof(hipfftComplex);
283 hipMemcpy(device_buffer_fp32_batch_, data, buffer_size, hipMemcpyDefault);
284
285 check_hipfft_error(hipfftExecC2C(plan_fp32_batch_,
286 device_buffer_fp32_batch_,
287 device_buffer_fp32_batch_,
288 HIPFFT_BACKWARD),
289 "hipfftExecC2C batched inverse");
290
291 hipMemcpy(data, device_buffer_fp32_batch_, buffer_size, hipMemcpyDefault);
292 }
293
294 // ── Batched FP64 FFT (hipfftPlanMany Z2Z) ─────────────────────────────
295
296 /**
297 * Batched forward FP64 FFT via hipfftPlanMany + hipfftExecZ2Z.
298 *
299 * All batch_size grids are transformed in a single hipFFT kernel launch.
300 */
301 void forward_transform_batch(Complex* data, int batch_size) const override {
302 ensure_batch_resources_fp64(batch_size);
303
304 const size_t buffer_size = static_cast<size_t>(total_grid_points_cached_) * batch_size *
305 sizeof(hipfftDoubleComplex);
306 hipMemcpy(device_buffer_fp64_batch_, data, buffer_size, hipMemcpyDefault);
307
308 check_hipfft_error(hipfftExecZ2Z(plan_fp64_batch_,
309 device_buffer_fp64_batch_,
310 device_buffer_fp64_batch_,
311 HIPFFT_FORWARD),
312 "hipfftExecZ2Z batched forward");
313
314 hipMemcpy(data, device_buffer_fp64_batch_, buffer_size, hipMemcpyDefault);
315 }
316
317 /**
318 * Batched inverse FP64 FFT (raw, unnormalized) via hipfftPlanMany + hipfftExecZ2Z.
319 *
320 * All batch_size grids are transformed in a single hipFFT kernel launch.
321 */
322 void inverse_transform_batch(Complex* data, int batch_size) const override {
323 ensure_batch_resources_fp64(batch_size);
324
325 const size_t buffer_size = static_cast<size_t>(total_grid_points_cached_) * batch_size *
326 sizeof(hipfftDoubleComplex);
327 hipMemcpy(device_buffer_fp64_batch_, data, buffer_size, hipMemcpyDefault);
328
329 check_hipfft_error(hipfftExecZ2Z(plan_fp64_batch_,
330 device_buffer_fp64_batch_,
331 device_buffer_fp64_batch_,
332 HIPFFT_BACKWARD),
333 "hipfftExecZ2Z batched inverse");
334
335 hipMemcpy(data, device_buffer_fp64_batch_, buffer_size, hipMemcpyDefault);
336 }
337
338 private:
339 SerialFFTGrid serial_fft_grid_;
340 hipfftHandle plan_fp64_ = 0;
341 hipfftHandle plan_fp32_ = 0;
342
343 // Persistent device buffers for single-grid hipFFT — allocated once in init().
344 hipfftComplex* device_buffer_fp32_ = nullptr;
345 hipfftDoubleComplex* device_buffer_fp64_ = nullptr;
346 int total_grid_points_cached_ = 0;
347
348 // Batched FP32 hipFFT resources — allocated lazily on first batch call.
349 mutable hipfftHandle plan_fp32_batch_ = 0;
350 mutable hipfftComplex* device_buffer_fp32_batch_ = nullptr;
351 mutable int current_fp32_batch_size_ = 0;
352
353 // Batched FP64 hipFFT resources — allocated lazily on first batch call.
354 mutable hipfftHandle plan_fp64_batch_ = 0;
355 mutable hipfftDoubleComplex* device_buffer_fp64_batch_ = nullptr;
356 mutable int current_fp64_batch_size_ = 0;
357
358 /**
359 * Ensure the batched FP32 hipFFT plan and device buffer are large enough.
360 */
361 void ensure_batch_resources(int batch_size) const {
362 if (batch_size == current_fp32_batch_size_) {
363 return;
364 }
365
366 if (plan_fp32_batch_ != 0) {
367 hipfftDestroy(plan_fp32_batch_);
368 plan_fp32_batch_ = 0;
369 }
370 if (device_buffer_fp32_batch_ != nullptr) {
371 hipFree(device_buffer_fp32_batch_);
372 device_buffer_fp32_batch_ = nullptr;
373 }
374
375 int dimensions[3] = {serial_fft_grid_.grid_dimension_1(),
376 serial_fft_grid_.grid_dimension_2(),
377 serial_fft_grid_.grid_dimension_3()};
378 int distance = total_grid_points_cached_;
379
380 check_hipfft_error(hipfftPlanMany(&plan_fp32_batch_,
381 3, // rank (3D)
382 dimensions, // [d1, d2, d3]
383 nullptr, // inembed (null = contiguous)
384 1, // istride
385 distance, // idist
386 nullptr, // onembed (null = contiguous)
387 1, // ostride
388 distance, // odist
389 HIPFFT_C2C, // type
390 batch_size), // batch
391 "hipfftPlanMany FP32 batch");
392
393 const size_t buffer_size =
394 static_cast<size_t>(total_grid_points_cached_) * batch_size * sizeof(hipfftComplex);
395 hipMalloc(&device_buffer_fp32_batch_, buffer_size);
396
397 current_fp32_batch_size_ = batch_size;
398 }
399
400 /**
401 * Ensure the batched FP64 hipFFT plan and device buffer are large enough.
402 */
403 void ensure_batch_resources_fp64(int batch_size) const {
404 if (batch_size == current_fp64_batch_size_) {
405 return;
406 }
407
408 if (plan_fp64_batch_ != 0) {
409 hipfftDestroy(plan_fp64_batch_);
410 plan_fp64_batch_ = 0;
411 }
412 if (device_buffer_fp64_batch_ != nullptr) {
413 hipFree(device_buffer_fp64_batch_);
414 device_buffer_fp64_batch_ = nullptr;
415 }
416
417 int dimensions[3] = {serial_fft_grid_.grid_dimension_1(),
418 serial_fft_grid_.grid_dimension_2(),
419 serial_fft_grid_.grid_dimension_3()};
420 int distance = total_grid_points_cached_;
421
422 check_hipfft_error(hipfftPlanMany(&plan_fp64_batch_,
423 3, // rank (3D)
424 dimensions, // [d1, d2, d3]
425 nullptr, // inembed (null = contiguous)
426 1, // istride
427 distance, // idist
428 nullptr, // onembed (null = contiguous)
429 1, // ostride
430 distance, // odist
431 HIPFFT_Z2Z, // type
432 batch_size), // batch
433 "hipfftPlanMany FP64 batch");
434
435 const size_t buffer_size = static_cast<size_t>(total_grid_points_cached_) * batch_size *
436 sizeof(hipfftDoubleComplex);
437 hipMalloc(&device_buffer_fp64_batch_, buffer_size);
438
439 current_fp64_batch_size_ = batch_size;
440 }
441};
442
443std::unique_ptr<GPUFFTGrid>
444create_hip_gpu_fft_grid(std::shared_ptr<execution::gpu::device::GPUDeviceBackend> device_backend) {
445 return std::make_unique<HIPGPUFFTGrid>(std::move(device_backend));
446}
447
448} // namespace gpu
449} // namespace q3
void inverse(Complex *data) const override
int local_grid_points() const override
int local_offset() const override
void forward(Complex *data) const override
void execute_forward_on_buffers() const override
void reduce_g_space(Complex *, int) const override
int grid_dimension_3() const override
int grid_dimension_1() const override
void init(int dimension_1, int dimension_2, int dimension_3, bool use_measured_planning=false) override
void inverse_raw(Complex *data) const override
Complex * output_buffer() const override
Complex * input_buffer() const override
void execute_inverse_raw_on_buffers() const override
int total_grid_points() const override
int grid_dimension_2() const override
int g_vector_to_fft_grid_index(int miller_1, int miller_2, int miller_3) const override
void prepare_forward_from_output() const override
execution::gpu::device::GPUDeviceBackend & device_backend()
Definition init.hpp:162
~HIPGPUFFTGrid() override
Definition hip.cpp:53
Complex * input_buffer() const override
Definition hip.cpp:149
Complex * output_buffer() const override
Definition hip.cpp:152
void reduce_g_space(Complex *data, int number_of_elements) const override
Definition hip.cpp:145
int g_vector_to_fft_grid_index(int miller_1, int miller_2, int miller_3) const override
Definition hip.cpp:141
HIPGPUFFTGrid(std::shared_ptr< execution::gpu::device::GPUDeviceBackend > device_backend)
Definition hip.cpp:50
void forward_single(ComplexSingle *data) const override
Definition hip.cpp:174
void inverse(Complex *data) const override
Definition hip.cpp:133
void inverse_transform(Complex *data) const override
Definition hip.cpp:230
void inverse_raw_single(ComplexSingle *data) const override
Definition hip.cpp:192
void inverse_raw_single_batch(ComplexSingle *data, int batch_size) const override
Definition hip.cpp:278
void execute_inverse_raw_on_buffers() const override
Definition hip.cpp:156
int local_offset() const override
Definition hip.cpp:126
int local_grid_points() const override
Definition hip.cpp:123
void forward_transform_batch(Complex *data, int batch_size) const override
Definition hip.cpp:301
void prepare_forward_from_output() const override
Definition hip.cpp:162
int grid_dimension_2() const override
Definition hip.cpp:114
void forward_single_batch(ComplexSingle *data, int batch_size) const override
Definition hip.cpp:256
void inverse_transform_batch(Complex *data, int batch_size) const override
Definition hip.cpp:322
int grid_dimension_3() const override
Definition hip.cpp:117
void forward_transform(Complex *data) const override
Definition hip.cpp:212
void forward(Complex *data) const override
Definition hip.cpp:130
int total_grid_points() const override
Definition hip.cpp:120
void init(int dimension_1, int dimension_2, int dimension_3, bool use_measured_planning=false) override
Definition hip.cpp:82
void inverse_raw(Complex *data) const override
Definition hip.cpp:136
void execute_forward_on_buffers() const override
Definition hip.cpp:159
int grid_dimension_1() const override
Definition hip.cpp:111
std::unique_ptr< GPUFFTGrid > create_hip_gpu_fft_grid(std::shared_ptr< execution::gpu::device::GPUDeviceBackend > device_backend)
Definition hip.cpp:444
std::complex< float > ComplexSingle
std::complex< double > Complex