Skip to content

Commit 77292ed

Browse files
committed
feat(cufft): improve API with type-state pattern of the FftType
1 parent 5078c7f commit 77292ed

3 files changed

Lines changed: 111 additions & 58 deletions

File tree

crates/cufft/src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
//! Rust wrapper for the [cuFFT library](https://docs.nvidia.com/cuda/cufft/).
22
//!
3-
//! Create a `FftPlan` with one of the plan constructors.
4-
//! Optionally attach a stream with `FftPlan::set_stream`
5-
//! Execute the plan with one of the `exec_*` methods.
6-
//! Plans are destroyed when they dropped.
3+
//! Create a `FftPlan<T>` with one of the plan constructors,
4+
//! where `T` implements the `FftType` trait (`C2C`, `R2C`, `C2R`, `Z2Z`, `D2Z`, `Z2D`).
5+
//! Optionally attach a stream with `FftPlan::set_stream`.
6+
//! Execute the plan with `FftPlan::exec`.
7+
//! Plans are destroyed when dropped.
78
//!
89
//! Raw bindgen bindings are available in `cufft_raw`.
910
1011
mod error;
1112
mod plan;
1213

1314
pub use error::{CufftError, IntoResult};
14-
pub use plan::{Direction, FftPlan, FftType};
15+
pub use plan::{C2C, C2R, D2Z, Direction, FftPlan, FftType, R2C, Z2D, Z2Z};

crates/cufft/src/plan.rs

Lines changed: 97 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,72 @@
1+
use std::marker::PhantomData;
12
use std::mem::MaybeUninit;
23

34
use cust::memory::GpuBuffer;
45

56
use crate::{CufftError, IntoResult};
67

7-
/// cuFFT transform type.
8-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9-
pub enum FftType {
10-
/// Single-precision real-to-complex.
11-
R2C,
12-
/// Single-precision complex-to-real.
13-
C2R,
14-
/// Single-precision complex-to-complex.
15-
C2C,
16-
/// Double-precision real-to-complex.
17-
D2Z,
18-
/// Double-precision complex-to-real.
19-
Z2D,
20-
/// Double-precision complex-to-complex.
21-
Z2Z,
8+
mod sealed {
9+
pub trait Sealed {}
2210
}
2311

24-
impl FftType {
25-
fn into_raw(self) -> cufft_raw::cufftType {
26-
use cufft_raw::cufftType::*;
27-
match self {
28-
FftType::R2C => CUFFT_R2C,
29-
FftType::C2R => CUFFT_C2R,
30-
FftType::C2C => CUFFT_C2C,
31-
FftType::D2Z => CUFFT_D2Z,
32-
FftType::Z2D => CUFFT_Z2D,
33-
FftType::Z2Z => CUFFT_Z2Z,
34-
}
12+
pub trait FftType: sealed::Sealed {
13+
#[doc(hidden)]
14+
fn fft_type() -> cufft_raw::cufftType;
15+
}
16+
17+
/// Marker type for single-precision complex-to-complex transforms.
18+
pub struct C2C;
19+
/// Marker type for single-precision real-to-complex transforms.
20+
pub struct R2C;
21+
/// Marker type for single-precision complex-to-real transforms.
22+
pub struct C2R;
23+
/// Marker type for double-precision real-to-complex transforms.
24+
pub struct D2Z;
25+
/// Marker type for double-precision complex-to-real transforms.
26+
pub struct Z2D;
27+
/// Marker type for double-precision complex-to-complex transforms.
28+
pub struct Z2Z;
29+
30+
impl sealed::Sealed for C2C {}
31+
impl sealed::Sealed for R2C {}
32+
impl sealed::Sealed for C2R {}
33+
impl sealed::Sealed for D2Z {}
34+
impl sealed::Sealed for Z2D {}
35+
impl sealed::Sealed for Z2Z {}
36+
37+
impl FftType for C2C {
38+
fn fft_type() -> cufft_raw::cufftType {
39+
cufft_raw::cufftType::CUFFT_C2C
40+
}
41+
}
42+
43+
impl FftType for R2C {
44+
fn fft_type() -> cufft_raw::cufftType {
45+
cufft_raw::cufftType::CUFFT_R2C
46+
}
47+
}
48+
49+
impl FftType for C2R {
50+
fn fft_type() -> cufft_raw::cufftType {
51+
cufft_raw::cufftType::CUFFT_C2R
52+
}
53+
}
54+
55+
impl FftType for D2Z {
56+
fn fft_type() -> cufft_raw::cufftType {
57+
cufft_raw::cufftType::CUFFT_D2Z
58+
}
59+
}
60+
61+
impl FftType for Z2D {
62+
fn fft_type() -> cufft_raw::cufftType {
63+
cufft_raw::cufftType::CUFFT_Z2D
64+
}
65+
}
66+
67+
impl FftType for Z2Z {
68+
fn fft_type() -> cufft_raw::cufftType {
69+
cufft_raw::cufftType::CUFFT_Z2Z
3570
}
3671
}
3772

@@ -55,25 +90,26 @@ impl Direction {
5590

5691
/// Wrapper for a `cufftHandle`.
5792
#[derive(Debug)]
58-
pub struct FftPlan {
59-
pub(crate) raw: cufft_raw::cufftHandle,
93+
pub struct FftPlan<T> {
94+
raw: cufft_raw::cufftHandle,
95+
_marker: PhantomData<T>,
6096
}
6197

62-
impl FftPlan {
98+
impl<T: FftType> FftPlan<T> {
6399
/// Creates a 1-D FFT plan.
64100
///
65101
/// # Reference
66102
///
67103
/// [cufftPlan1d](https://docs.nvidia.com/cuda/cufft/index.html#cufftplan1d)
68-
pub fn plan_1d(nx: i32, fft_type: FftType, batch: i32) -> Result<Self, CufftError> {
104+
pub fn plan_1d(nx: i32, batch: i32) -> Result<Self, CufftError> {
69105
let mut raw = MaybeUninit::uninit();
70106

71107
unsafe {
72-
cufft_raw::cufftPlan1d(raw.as_mut_ptr(), nx, fft_type.into_raw(), batch)
73-
.into_result()?;
108+
cufft_raw::cufftPlan1d(raw.as_mut_ptr(), nx, T::fft_type(), batch).into_result()?;
74109

75110
Ok(Self {
76111
raw: raw.assume_init(),
112+
_marker: PhantomData,
77113
})
78114
}
79115
}
@@ -83,14 +119,15 @@ impl FftPlan {
83119
/// # Reference
84120
///
85121
/// [cufftPlan2d](https://docs.nvidia.com/cuda/cufft/index.html#cufftplan2d)
86-
pub fn plan_2d(nx: i32, ny: i32, fft_type: FftType) -> Result<Self, CufftError> {
122+
pub fn plan_2d(nx: i32, ny: i32) -> Result<Self, CufftError> {
87123
let mut raw = MaybeUninit::uninit();
88124

89125
unsafe {
90-
cufft_raw::cufftPlan2d(raw.as_mut_ptr(), nx, ny, fft_type.into_raw()).into_result()?;
126+
cufft_raw::cufftPlan2d(raw.as_mut_ptr(), nx, ny, T::fft_type()).into_result()?;
91127

92128
Ok(Self {
93129
raw: raw.assume_init(),
130+
_marker: PhantomData,
94131
})
95132
}
96133
}
@@ -100,15 +137,15 @@ impl FftPlan {
100137
/// # Reference
101138
///
102139
/// [cufftPlan3d](https://docs.nvidia.com/cuda/cufft/index.html#cufftplan3d)
103-
pub fn plan_3d(nx: i32, ny: i32, nz: i32, fft_type: FftType) -> Result<Self, CufftError> {
140+
pub fn plan_3d(nx: i32, ny: i32, nz: i32) -> Result<Self, CufftError> {
104141
let mut raw = MaybeUninit::uninit();
105142

106143
unsafe {
107-
cufft_raw::cufftPlan3d(raw.as_mut_ptr(), nx, ny, nz, fft_type.into_raw())
108-
.into_result()?;
144+
cufft_raw::cufftPlan3d(raw.as_mut_ptr(), nx, ny, nz, T::fft_type()).into_result()?;
109145

110146
Ok(Self {
111147
raw: raw.assume_init(),
148+
_marker: PhantomData,
112149
})
113150
}
114151
}
@@ -128,7 +165,6 @@ impl FftPlan {
128165
onembed: Option<&[i32]>,
129166
ostride: i32,
130167
odist: i32,
131-
fft_type: FftType,
132168
batch: i32,
133169
) -> Result<Self, CufftError> {
134170
let mut raw = MaybeUninit::uninit();
@@ -147,18 +183,19 @@ impl FftPlan {
147183
onembed_ptr,
148184
ostride,
149185
odist,
150-
fft_type.into_raw(),
186+
T::fft_type(),
151187
batch,
152188
)
153189
.into_result()?;
154190

155191
Ok(Self {
156192
raw: raw.assume_init(),
193+
_marker: PhantomData,
157194
})
158195
}
159196
}
160197

161-
/// Set the CUDA stream for the plan.
198+
/// Sets the CUDA stream for the plan.
162199
///
163200
/// # Reference
164201
///
@@ -177,13 +214,15 @@ impl FftPlan {
177214
pub fn as_raw(&self) -> cufft_raw::cufftHandle {
178215
self.raw
179216
}
217+
}
180218

219+
impl FftPlan<C2C> {
181220
/// Executes a single-precision C2C FFT.
182221
///
183222
/// # Reference
184223
///
185224
/// [cufftExecC2C](https://docs.nvidia.com/cuda/cufft/index.html#cufftexecc2c-and-cufftexecz2z)
186-
pub fn exec_c2c(
225+
pub fn exec(
187226
&self,
188227
idata: &impl GpuBuffer<cufft_raw::cufftComplex>,
189228
odata: &mut impl GpuBuffer<cufft_raw::cufftComplex>,
@@ -199,13 +238,15 @@ impl FftPlan {
199238
.into_result()
200239
}
201240
}
241+
}
202242

243+
impl FftPlan<R2C> {
203244
/// Executes a single-precision R2C FFT.
204245
///
205246
/// # Reference
206247
///
207248
/// [cufftExecR2C](https://docs.nvidia.com/cuda/cufft/index.html#cufftexecr2c-and-cufftexecd2z)
208-
pub fn exec_r2c(
249+
pub fn exec(
209250
&self,
210251
idata: &impl GpuBuffer<cufft_raw::cufftReal>,
211252
odata: &mut impl GpuBuffer<cufft_raw::cufftComplex>,
@@ -219,13 +260,15 @@ impl FftPlan {
219260
.into_result()
220261
}
221262
}
263+
}
222264

265+
impl FftPlan<C2R> {
223266
/// Executes a single-precision C2R inverse FFT.
224267
///
225268
/// # Reference
226269
///
227270
/// [cufftExecC2R](https://docs.nvidia.com/cuda/cufft/index.html#cufftexecc2r-and-cufftexecz2d)
228-
pub fn exec_c2r(
271+
pub fn exec(
229272
&self,
230273
idata: &impl GpuBuffer<cufft_raw::cufftComplex>,
231274
odata: &mut impl GpuBuffer<cufft_raw::cufftReal>,
@@ -239,13 +282,15 @@ impl FftPlan {
239282
.into_result()
240283
}
241284
}
285+
}
242286

287+
impl FftPlan<Z2Z> {
243288
/// Executes a double-precision Z2Z FFT.
244289
///
245290
/// # Reference
246291
///
247292
/// [cufftExecZ2Z](https://docs.nvidia.com/cuda/cufft/index.html#cufftexecc2c-and-cufftexecz2z)
248-
pub fn exec_z2z(
293+
pub fn exec(
249294
&self,
250295
idata: &impl GpuBuffer<cufft_raw::cufftDoubleComplex>,
251296
odata: &mut impl GpuBuffer<cufft_raw::cufftDoubleComplex>,
@@ -261,13 +306,15 @@ impl FftPlan {
261306
.into_result()
262307
}
263308
}
309+
}
264310

311+
impl FftPlan<D2Z> {
265312
/// Executes a double-precision D2Z FFT.
266313
///
267314
/// # Reference
268315
///
269316
/// [cufftExecD2Z](https://docs.nvidia.com/cuda/cufft/index.html#cufftexecr2c-and-cufftexecd2z)
270-
pub fn exec_d2z(
317+
pub fn exec(
271318
&self,
272319
idata: &impl GpuBuffer<cufft_raw::cufftDoubleReal>,
273320
odata: &mut impl GpuBuffer<cufft_raw::cufftDoubleComplex>,
@@ -281,13 +328,15 @@ impl FftPlan {
281328
.into_result()
282329
}
283330
}
331+
}
284332

333+
impl FftPlan<Z2D> {
285334
/// Executes a double-precision Z2D inverse FFT.
286335
///
287336
/// # Reference
288337
///
289338
/// [cufftExecZ2D](https://docs.nvidia.com/cuda/cufft/index.html#cufftexecc2r-and-cufftexecz2d)
290-
pub fn exec_z2d(
339+
pub fn exec(
291340
&self,
292341
idata: &impl GpuBuffer<cufft_raw::cufftDoubleComplex>,
293342
odata: &mut impl GpuBuffer<cufft_raw::cufftDoubleReal>,
@@ -303,12 +352,12 @@ impl FftPlan {
303352
}
304353
}
305354

306-
impl Drop for FftPlan {
355+
impl<T> Drop for FftPlan<T> {
307356
/// Destroys the plan.
308357
///
309358
/// # Reference
310359
///
311-
/// [cufftDestroy)(https://docs.nvidia.com/cuda/cufft/index.html#cufftdestroy)
360+
/// [cufftDestroy](https://docs.nvidia.com/cuda/cufft/index.html#cufftdestroy)
312361
fn drop(&mut self) {
313362
unsafe {
314363
let _ = cufft_raw::cufftDestroy(self.raw);

examples/fft/src/main.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::f32::consts::PI;
22

3-
use cufft::{Direction, FftPlan, FftType};
3+
use cufft::{C2C, Direction, FftPlan};
44
use cufft_raw::float2;
55
use cust::memory::{CopyDestination, DeviceBuffer};
66

@@ -26,11 +26,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
2626
let device_signal = DeviceBuffer::from_slice(&host_signal)?;
2727
let mut device_spectrum = unsafe { DeviceBuffer::<float2>::uninitialized(FFT_SIZE)? };
2828

29-
let plan = FftPlan::plan_1d(FFT_SIZE as i32, FftType::C2C, 1)?;
29+
let plan = FftPlan::<C2C>::plan_1d(FFT_SIZE as i32, 1)?;
3030

3131
// Forward FFT.
3232

33-
plan.exec_c2c(&device_signal, &mut device_spectrum, Direction::Forward)?;
33+
plan.exec(&device_signal, &mut device_spectrum, Direction::Forward)?;
3434

3535
let mut host_spectrum = vec![float2 { x: 0.0, y: 0.0 }; FFT_SIZE];
3636
device_spectrum.copy_to(&mut host_spectrum)?;
@@ -51,13 +51,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
5151
println!("input frequency bin : {FREQUENCY_BIN}");
5252
println!("peak frequency bin : {peak}");
5353

54-
assert_eq!(peak, FREQUENCY_BIN, "Input frequency bin does not equal peak frequency bin.");
54+
assert_eq!(
55+
peak, FREQUENCY_BIN,
56+
"Input frequency bin does not equal peak frequency bin."
57+
);
5558

5659
// Inverse FFT then normalize by FFT_SIZE to recover the original signal.
5760

5861
let mut device_recovered = unsafe { DeviceBuffer::<float2>::uninitialized(FFT_SIZE)? };
5962

60-
plan.exec_c2c(&device_spectrum, &mut device_recovered, Direction::Inverse)?;
63+
plan.exec(&device_spectrum, &mut device_recovered, Direction::Inverse)?;
6164

6265
let mut host_recovered = vec![float2 { x: 0.0, y: 0.0 }; FFT_SIZE];
6366

0 commit comments

Comments
 (0)