Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Deepak-Sangle/TornadoVM/llms.txt

Use this file to discover all available pages before exploring further.

TornadoVM’s cuFFT integration brings NVIDIA’s highly optimized FFT library into the same TaskGraph programming model you use for JIT-compiled Java kernels. FFT transforms run as library tasks that share TornadoVM-managed device buffers with adjacent JIT kernels — no host round-trips between the transform and the spectral-domain processing code. This is particularly powerful for filter pipelines: you can express a complete FFT → filter → inverse FFT → normalize workflow as a single graph where none of the intermediate results ever leave the GPU.
Prerequisite: CUDA Toolkit with cuFFT (libcufft). cuFFT ships inside the CUDA Toolkit — no separate installation is needed. Build with make BACKEND=cuda.

Factory Methods

All factories are static methods on uk.ac.manchester.tornado.cufft.CuFft. Pass them as the second argument to taskGraph.libraryTask(id, factory, args...).

1D Transforms

FactorycuFFT CallInput / Output TypesNotes
cufftForwardC2C(in, out, n, batch)cufftExecC2C(FORWARD)FloatArray (interleaved re/im)batch contiguous transforms of length n
cufftInverseC2C(in, out, n, batch)cufftExecC2C(INVERSE)FloatArray (interleaved re/im)Unnormalized: inverse(forward(x)) = n·x
cufftForwardR2C(in, out, n, batch)cufftExecR2Cin: FloatArray (reals); out: FloatArray (complex, Hermitian half)Output length is (n/2+1)·batch complex elements
cufftInverseC2R(in, out, n, batch)cufftExecC2Rin: FloatArray (Hermitian complex); out: FloatArray (reals)Unnormalized; inverse of R2C
cufftForwardZ2Z(in, out, n, batch)cufftExecZ2Z(FORWARD)DoubleArray (interleaved re/im)FP64 complex-to-complex
cufftInverseZ2Z(in, out, n, batch)cufftExecZ2Z(INVERSE)DoubleArray (interleaved re/im)FP64 inverse, unnormalized

2D Transforms

FactorycuFFT CallInput / Output TypesNotes
cufftForward2dC2C(in, out, nx, ny)cufftExecC2C (2D plan)FloatArray (interleaved re/im)Row-major nx × ny grid
cufftInverse2dC2C(in, out, nx, ny)cufftExecC2C(INVERSE) (2D plan)FloatArray (interleaved re/im)Unnormalized inverse

Data Layout

Complex Arrays (C2C, Z2Z)

Complex data uses interleaved (real, imaginary) pairs. A signal of n complex samples requires a FloatArray of length 2n. Access element i as array.get(2*i) (real) and array.get(2*i+1) (imaginary).

Real-to-Complex (R2C)

For R2C, the input is a FloatArray of n real samples per batch. The output is a FloatArray of 2*(n/2+1) complex samples per batch (Hermitian symmetry halves the spectrum). The inverse C2R restores the original n reals.

Automatic Plan Caching

cuFFT plans are created once per (transform type, shape) key and cached in the per-(device, execution-plan) context. The provider’s prepare() hook runs before CUDA Graph capture starts, so all plan allocations happen outside the capture region. Subsequent dispatch() calls allocate nothing — they simply reuse the cached plan.
Plans are destroyed automatically when the TornadoExecutionPlan closes (try-with-resources). Reuse the same TornadoExecutionPlan across loop iterations to avoid repeated plan creation.

FFT Filter Pipeline Example

The following example implements a complete GPU-resident low-pass filter: a real-to-complex forward FFT, a JIT kernel that zeroes high-frequency bins, a complex-to-real inverse FFT, and a JIT normalization kernel — all in one TaskGraph with no host-side data movement between steps.
import uk.ac.manchester.tornado.api.*;
import uk.ac.manchester.tornado.api.enums.DataTransferMode;
import uk.ac.manchester.tornado.api.types.arrays.FloatArray;
import uk.ac.manchester.tornado.cufft.CuFft;

int n       = 65536;
int cutoff  = n / 4;              // keep only the lowest quarter of frequencies
int bins    = n / 2 + 1;         // Hermitian output length per R2C transform
int batch   = 1;

FloatArray signal   = new FloatArray(n);          // real input
FloatArray spectrum = new FloatArray(2 * bins);   // complex spectrum (interleaved)
FloatArray filtered = new FloatArray(n);          // real output

// ... fill signal with data ...

TaskGraph graph = new TaskGraph("filter")
    .transferToDevice(DataTransferMode.EVERY_EXECUTION, signal)
    .libraryTask("fwd",       CuFft::cufftForwardR2C,    // R2C forward FFT
            signal, spectrum, n, batch)
    .task("lowpass",          Filters::lowPass,           // JIT: zero high-freq bins
            spectrum, cutoff, bins)
    .libraryTask("inv",       CuFft::cufftInverseC2R,    // C2R inverse FFT
            spectrum, filtered, n, batch)
    .task("normalize",        Filters::scaleBy,           // JIT: divide by n
            filtered, 1.0f / n)
    .transferToHost(DataTransferMode.EVERY_EXECUTION, filtered);

try (TornadoExecutionPlan plan = new TornadoExecutionPlan(graph.snapshot())) {
    plan.execute();
}
The lowpass JIT kernel runs entirely on-device, consuming the spectrum buffer produced by cuFFT and writing back to the same buffer. The inverse cuFFT immediately consumes that modified buffer — zero bytes move through the host.

CUDA Graph Capture

cuFFT library tasks are fully CUDA Graph compatible. Enable graph capture with plan.withCUDAGraph() on the execution plan — iteration 0 captures the graph (including the FFT calls), and all subsequent iterations replay it with a single cuGraphLaunch.
try (TornadoExecutionPlan plan = new TornadoExecutionPlan(graph.snapshot())) {
    plan.withCUDAGraph();   // capture on iteration 0, replay thereafter
    for (int i = 0; i < 1000; i++) {
        // Update signal contents here (host-side array)
        plan.execute();
    }
}
Run the CUDA Graph round-trip test:
tornado-test -V uk.ac.manchester.tornado.unittests.cufft.TestCuFft#testRoundTripWithCudaGraph

2D FFT Example

For image processing and spectral analysis on 2D grids, use cufftForward2dC2C and cufftInverse2dC2C with row-major nx × ny layout.
int nx = 512, ny = 512;
FloatArray image    = new FloatArray(2 * nx * ny);   // complex input (interleaved)
FloatArray spectrum = new FloatArray(2 * nx * ny);   // complex spectrum

TaskGraph graph = new TaskGraph("fft2d")
    .transferToDevice(DataTransferMode.FIRST_EXECUTION, image)
    .libraryTask("fwd2d",  CuFft::cufftForward2dC2C,  image, spectrum, nx, ny)
    .task("process",       ImageOps::processSpectrum,  spectrum)
    .libraryTask("inv2d",  CuFft::cufftInverse2dC2C,  spectrum, image, nx, ny)
    .task("scale",         ImageOps::scaleBy,          image, 1.0f / (nx * ny))
    .transferToHost(DataTransferMode.EVERY_EXECUTION, image);

Performance

Benchmark: n = 65536

BenchmarkFft compares three implementations on an RTX 4090 at n = 65536 with 20 warm iterations:
ImplementationTimevs JIT DFT
Sequential Java DFT228,819 msbaseline
TornadoVM JIT DFT kernel63.4 ms3,611× faster
cuFFT library task0.080 ms793× faster than JIT
The cuFFT library task is 793× faster than the best JIT-compiled DFT kernel and nearly 3 million× faster than sequential Java.

Run the Benchmark

# args: [n] [iterations]
tornado -m tornado.cufft/\
uk.ac.manchester.tornado.cufft.tests.BenchmarkFft \
65536 20
# Full frequency-filter pipeline example
tornado -m tornado.cufft/\
uk.ac.manchester.tornado.cufft.tests.FrequencyFilterExample \
4096 16

Unit Tests and Runnable Examples

# Full cuFFT test suite (auto-skips without CUDA backend / libtornado-cufft)
tornado-test -V uk.ac.manchester.tornado.unittests.cufft.TestCuFft

Known Limitations

The following cuFFT features are not yet bound in the current provider. They remain available by implementing a custom provider if needed:
  • cufftPlanMany with advanced strided or embedded layouts
  • 3D FFT plans
  • D2Z / Z2D (double-precision real-to-complex)
  • FP16/BF16 via cufftXtMakePlanMany
  • LTO callbacks (cufftXtSetJITCallback)
  • Explicit workspace control (cufftSetWorkArea)
  • Multi-GPU cufftXt API
The cufftInverseC2C and cufftInverseC2R results are unnormalized. A signal of length n satisfies inverse(forward(x)) = n·x. Normalize by multiplying by 1.0f / n after the inverse transform — as shown in the filter pipeline example above using a JIT scaleBy task.

Build docs developers (and LLMs) love