FFT Processing Blocks
Relevant source files
The following files were used as context for generating this wiki page:
The FFT processing blocks in gr-cuda provide high-performance Fast Fourier Transform capabilities by leveraging NVIDIA GPUs. These blocks are designed to operate within a zero-copy GPU pipeline, allowing data to move between blocks via device pointers without manual host-to-device transfers in the work() functions. The system supports two primary implementations: a C++ implementation utilizing the NVIDIA cuFFT library and a Python implementation utilizing CuPy.
Sources: README.md:1-15, include/gnuradio/cuda/fft.h:17-27
Architecture and Implementations
The project provides two distinct paths for FFT execution, allowing developers to choose between the performance of C++ or the development speed of Python while maintaining nearly identical performance (within 5%).
C++ cuFFT Implementation
The C++ implementation, defined in gr::cuda::fft, inherits from cuda_block. It utilizes the cufft.h library to perform transforms. This implementation manages a cache of cufftHandle plans to optimize performance across varying batch sizes and ensures that GPU staging buffers are appropriately allocated.
Sources: lib/fft_impl.h:15-54, include/gnuradio/cuda/fft.h:29-50
Python CuPy Implementation
The Python implementation, cuda.fft_cupy, inherits from cuda.sync_block. It uses CuPy's FFT module to perform operations on GPU-resident arrays. This block is particularly efficient for rapid prototyping as it tracks within 5% of the C++ implementation's throughput.
Sources: README.md:58-61, python/cuda/fft_cupy.py
High-Level Class Structure
The following diagram illustrates the relationship between the FFT interfaces and their implementations.
Functional Capabilities
Both FFT implementations support a standard set of signal processing features:
- Transform Directions: Support for both forward (FFT) and inverse (IFFT) transforms.
- Input Types: Support for
complex64vectors or realfloat32inputs (with float-to-complex conversion). - Windowing: Application of real-valued window coefficients of size
fft_size. - FFT Shift: Optional application of
fftshiftto center the zero-frequency component.
Sources: include/gnuradio/cuda/fft.h:23-27, python/cuda/qa_fft.py:38-42
Configuration Parameters
| Parameter | Type | Description |
|---|---|---|
fft_size | size_t | Vector length and size of the FFT. |
forward | bool | True for forward FFT, False for inverse FFT. |
window | std::vector<float> | Real-valued window coefficients (must match fft_size). |
shift | bool | True to apply fftshift logic. |
real_input | bool | True for float32 input (performs float-to-complex FFT). |
Sources: include/gnuradio/cuda/fft.h:38-49, lib/fft_impl.h:26-32
Data Flow and Synchronization
The FFT blocks participate in the cuda_buffer zero-copy pipeline. This mechanism uses CUDA events (wait_device_ready and mark_device_ready) to synchronize execution between blocks without CPU intervention.
Execution Sequence
The following diagram shows how the FFT block handles a typical work cycle in the C++ implementation.
Performance Benchmarking
The project includes a dedicated benchmarking tool, benchmark_gr_cuda_fft.py, to measure throughput for cuFFT (C++), CuPy FFT (Python), and FFTW (CPU baseline). Benchmarks are conducted entirely on-device (using cuda.null_source and cuda.null_sink) to isolate compute performance from PCIe overhead.
Representative Throughput (RTX PRO 6000)
| Engine | Performance |
|---|---|
| cuFFT C64→C64 (C++) | 92.8 Gsps |
| CuPy FFT C64→C64 (Python) | 88.3 Gsps |
| FFTW C64→C64 (CPU) | 1.01 Gsps |
Sources: README.md:76-85, apps/benchmark_gr_cuda_fft.py:16-25
Implementation Details
C++ Internal State
The fft_impl class maintains a private plan cache using an unordered_map and a mutex to thread-safely retrieve cuFFT handles for different batch sizes. It also manages a GPU staging buffer (d_work_dev) to facilitate the transform process.
private:
cufftHandle get_plan(int batch);
void ensure_work_buffers(size_t total_items);
const size_t d_fft_size;
const bool d_forward;
const bool d_shift;
float* d_window_dev;
cufftComplex* d_work_dev;
std::unordered_map<int, cufftHandle> d_plan_cache;
std::mutex d_plan_mutex;
Sources: lib/fft_impl.h:40-52
Python Block Example
Writing a custom FFT-related block in Python involves inheriting from cuda.sync_block, where the input and output items are automatically provided as CuPy arrays.
import cupy as cp
from gnuradio import cuda
class my_fft_wrapper(cuda.sync_block):
def work(self, input_items, output_items):
# input_items[0] is a CuPy array on the GPU
output_items[0][:] = cp.fft.fft(input_items[0])
return len(output_items[0])
Sources: README.md:33-45, python/cuda/fft_cupy.py
Conclusion
FFT Processing Blocks in gr-cuda bridge the gap between high-level GNU Radio flowgraphs and low-level GPU acceleration. By providing both cuFFT and CuPy-based implementations, the system offers extreme performance for production environments while maintaining a flexible, Python-friendly interface for development. The integration with cuda_buffer ensures that these blocks function as part of a high-throughput, low-latency GPU signal processing pipeline.