Performance Benchmarks
Relevant source files
The following files were used as context for generating this wiki page:
Performance Benchmarks in gr-cuda serve to quantify the efficiency of GPU-accelerated signal processing compared to traditional CPU-based workflows. The project provides dedicated benchmarking scripts to measure raw PCIe data transfer throughput and computational kernel performance, such as Fast Fourier Transforms (FFT). These benchmarks highlight the advantages of the Zero-copy GPU pipeline, which utilizes cuda_buffer for automatic host-device transfers and CUDA-event synchronization.
The scope of these benchmarks includes measuring Host-to-Device (H2D), Device-to-Host (D2H), and full round-trip transfers, as well as comparing C++ CUDA implementations against Python-based CuPy blocks. Results indicate that Python-wrapped GPU blocks typically perform within 5% of hand-written C++ CUDA blocks, enabling high-performance development without leaving the Python ecosystem.
Sources: README.md:12-25, README.md:104-118
Data Transfer Throughput
The transfer benchmarks measure the effective bandwidth of the cuda_buffer pipeline across the PCIe bus. This is critical for understanding the overhead of moving data between the host CPU memory and the NVIDIA GPU device memory.
Benchmark Configurations
The system evaluates three primary data flow modes to isolate performance bottlenecks in different segments of the pipeline:
| Mode | Description | Components Involved |
|---|---|---|
| H2D | Host-to-Device | blocks.null_source -> cuda.nop -> cuda.null_sink |
| D2H | Device-to-Host | cuda.null_source -> cuda.nop -> blocks.null_sink |
| Full | Round-trip | blocks.null_source -> cuda.nop (H2D) -> cuda.nop (D2H) -> blocks.null_sink |
Sources: apps/benchmark_gr_cuda_transfer.py:33-66
Transfer Logic and Synchronization
To ensure accurate measurement, the cuda.nop block is used to force DMA event synchronization. Without this, software counters may outpace actual hardware DMA transfers. Large output_multiple values are enforced (defaulting to 32 times the buffer length) to ensure the GNU Radio scheduler issues large work() calls, which maximizes PCIe throughput by utilizing large DMA packets.
The following diagram illustrates the H2D transfer pipeline used in benchmarks:
The H2D benchmark flow uses a CPU source to write into a cuda_buffer, where a NOP block triggers the DMA transfer to the GPU device.
Sources: apps/benchmark_gr_cuda_transfer.py:33-46, apps/benchmark_gr_cuda_transfer.py:70-74
Computational Performance (FFT)
The FFT benchmark compares three distinct engines: cuFFT (C++ GPU), CuPy FFT (Python GPU), and FFTW (CPU baseline). To isolate compute performance from PCIe overhead, the GPU benchmarks run entirely on-device using cuda.null_source and cuda.null_sink.
Benchmark Engines and Metrics
Throughput is measured in Gsps (Giga-samples per second) and FFTs/sec across a range of FFT sizes (typically $2^8$ to $2^{20}$ points).
- cuFFT: Uses native NVIDIA libraries via C++ bindings.
- CuPy: Uses Python-based GPU acceleration, tracking within 5% of C++ performance.
- FFTW: Provides a baseline for CPU-bound signal processing.
Throughput Comparison Table
Baseline measurements performed on high-end hardware (NVIDIA DGX Spark GB10 and RTX PRO 6000 Blackwell):
| Benchmark | GB10 | RTX PRO 6000 |
|---|---|---|
| H2D transfer | 58.3 GB/s | 54.1 GB/s |
| D2H transfer | 59.1 GB/s | 56.4 GB/s |
| cuFFT C64→C64 | 13.9 Gsps | 92.8 Gsps |
| CuPy FFT C64→C64 | 13.5 Gsps | 88.3 Gsps |
| FFTW (CPU) | 0.45 Gsps | 1.01 Gsps |
Sources: README.md:104-118, apps/benchmark_gr_cuda_fft.py:11-25
Architectural Performance Drivers
The high performance of gr-cuda is attributed to several architectural features designed to minimize synchronization overhead and maximize hardware utilization.
Buffer Management and Compaction
The cuda_buffer implementation uses a BUFFER_MULTIPLE (often set to 16 or 32 in benchmarks) to provide the producer headroom over the consumer. This reduces the frequency of "buffer compaction" in the virtual memory management (VMM) implementation, avoiding costly GPU pipeline drains that occur when a buffer is blocked.
Sources: apps/benchmark_gr_cuda_fft.py:39-42, apps/benchmark_gr_cuda_transfer.py:22-25
Synchronization Mechanisms
Synchronization is handled GPU-side via CUDA events, preventing the CPU from blocking during kernel execution.
The synchronization flow ensures the CPU thread only enqueues work and records readiness events, allowing the GPU to process data stream segments back-to-back.
Load Testing
The gr::cuda::load block is a specialized utility used for profiling. It introduces a configurable for-loop load on the GPU processing to simulate complex kernels, allowing developers to see gains in data transfer efficiency using custom buffers versus artificial compute bottlenecks.
Sources: include/gnuradio/cuda/load.h:32-41
Summary
The performance benchmarks of gr-cuda demonstrate that GPU-accelerated signal processing provides an order-of-magnitude improvement over CPU-based FFTW (e.g., ~92 Gsps vs ~1 Gsps on Blackwell hardware). The zero-copy architecture and CUDA VMM implementation enable high-bandwidth PCIe utilization (up to 59 GB/s), while the Python/CuPy integration ensures that these performance gains are accessible with minimal overhead compared to native C++ implementations.
Sources: README.md:113-118, apps/benchmark_gr_cuda_transfer.py:165-170