Description
FFTW is an open-source C library dedicated to the fast computation of the discrete Fourier transform (DFT) in one or multiple dimensions.
It provides functions to compute complex and real Fourier transforms, as well as their inverse transforms, while automatically optimizing the algorithms used according to the data size and the underlying hardware architecture.
FFTW relies on a planning mechanism (“planning”) that evaluates different computation strategies in order to select the most efficient one for a given problem, providing excellent performance across a wide range of platforms.
The goal of FFTW is to provide a portable, robust, and highly optimized library suitable for applications in signal processing, image processing, scientific simulations, spectral analysis, and more generally, all fields requiring efficient Fourier transform computations.
Set up the environment
ml fftw
- Available version(s):
- 3.3.10
- 3.3.11 (Built with GCC 15.2.0) – default
- 3.3.11-intelmpi-2021.16 (Built with GCC 15.2.0 and Intel MPI 2021.16)
Tutorials
Use case: Simple 1D FFT
- Create the file
fftw_simple.c
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#include <fftw3-mpi.h>
int main(int argc, char **argv)
{
MPI_Init(&argc, &argv);
fftw_mpi_init();
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
// Global size of the FFT transform
ptrdiff_t N = 1024;
// MPI data distribution variables
ptrdiff_t local_n0;
ptrdiff_t local_0_start;
// Additional variables required by FFTW MPI
ptrdiff_t local_n1;
ptrdiff_t local_1_start;
/*
FFTW automatically computes
the data distribution among MPI processes
*/
ptrdiff_t alloc_local =
fftw_mpi_local_size_1d(
N,
MPI_COMM_WORLD,
FFTW_FORWARD,
FFTW_ESTIMATE,
&local_n0,
&local_0_start,
&local_n1,
&local_1_start
);
/*
Local memory allocation
Each process owns only
its part of the global array
*/
fftw_complex *data;
data = fftw_malloc(
sizeof(fftw_complex) * alloc_local
);
if (data == NULL)
{
fprintf(stderr,
"Memory allocation error\n");
MPI_Finalize();
return 1;
}
/*
Initialize local data
Each process initializes its own portion
*/
for (ptrdiff_t i = 0; i < local_n0; i++)
{
ptrdiff_t global_i =
local_0_start + i;
data[i][0] = (double)global_i; // Real part
data[i][1] = 0.0; // Imaginary part
}
/*
Create the MPI FFT plan
*/
fftw_plan plan =
fftw_mpi_plan_dft_1d(
N,
data,
data,
MPI_COMM_WORLD,
FFTW_FORWARD,
FFTW_ESTIMATE
);
if (plan == NULL)
{
fprintf(stderr,
"Error creating FFT plan\n");
fftw_free(data);
MPI_Finalize();
return 1;
}
/*
Execute the parallel FFT
*/
fftw_execute(plan);
/*
Display output only from the master process
*/
if (rank == 0)
{
printf("FFT completed using %d MPI processes\n",
size);
}
/*
Free allocated resources
*/
fftw_destroy_plan(plan);
fftw_free(data);
MPI_Finalize();
return 0;
}
- Compile
ml gcc fftw
gcc fftw_simple.c -o fftw_simple -lfftw3 -lm -o fftw_simple
- Run within a job:
srun ./fftw_simple
Use Case: Parallel 1D FFT Computation with FFTW and MPI
- Create the file
fftw_mpi.c
#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#include <fftw3-mpi.h>
int main(int argc, char **argv)
{
MPI_Init(&argc, &argv);
fftw_mpi_init();
int rank, size;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
// Global FFT size
ptrdiff_t N = 1024;
// MPI local data distribution variables
ptrdiff_t local_n0;
ptrdiff_t local_0_start;
ptrdiff_t local_n1;
ptrdiff_t local_1_start;
/*
FFTW automatically computes
the data distribution among MPI processes
*/
ptrdiff_t alloc_local =
fftw_mpi_local_size_1d(
N,
MPI_COMM_WORLD,
FFTW_FORWARD,
FFTW_ESTIMATE,
&local_n0,
&local_0_start,
&local_n1,
&local_1_start
);
fftw_complex *data;
data = fftw_malloc(sizeof(fftw_complex) * alloc_local);
if (!data)
{
fprintf(stderr,
"Memory allocation error on rank %d\n",
rank);
MPI_Finalize();
return 1;
}
/*
Local data initialization
Each MPI process initializes its own portion
*/
for (ptrdiff_t i = 0; i < local_n0; i++)
{
ptrdiff_t global_i = local_0_start + i;
data[i][0] = (double)global_i; // Real part
data[i][1] = 0.0; // Imaginary part
}
/*
Display MPI data distribution
*/
MPI_Barrier(MPI_COMM_WORLD);
for (int r = 0; r < size; r++)
{
if (rank == r)
{
printf("\nMPI process %d/%d\n", rank, size);
printf(" Global index range: %td -> %td\n",
local_0_start,
local_0_start + local_n0 - 1);
printf(" Number of local points: %td\n",
local_n0);
printf(" Example before FFT:\n");
for (int i = 0; i < 5 && i < local_n0; i++)
{
printf(" data[%d] = %f + %fi\n",
i,
data[i][0],
data[i][1]);
}
fflush(stdout);
}
MPI_Barrier(MPI_COMM_WORLD);
}
/*
Create the distributed FFT plan
*/
fftw_plan plan =
fftw_mpi_plan_dft_1d(
N,
data,
data,
MPI_COMM_WORLD,
FFTW_FORWARD,
FFTW_ESTIMATE
);
if (!plan)
{
printf("FFT plan creation error on rank %d\n",
rank);
fftw_free(data);
MPI_Finalize();
return 1;
}
double t1 = MPI_Wtime();
/*
Execute the parallel FFT computation
*/
fftw_execute(plan);
double t2 = MPI_Wtime();
/*
Display FFT results
*/
MPI_Barrier(MPI_COMM_WORLD);
for (int r = 0; r < size; r++)
{
if (rank == r)
{
printf("\nFFT results on rank %d\n", rank);
for (int i = 0; i < 5 && i < local_n0; i++)
{
printf(" FFT[%td] = %f + %fi\n",
local_0_start + i,
data[i][0],
data[i][1]);
}
printf("Local computation time: %f seconds\n",
t2 - t1);
fflush(stdout);
}
MPI_Barrier(MPI_COMM_WORLD);
}
if (rank == 0)
{
printf("\nGlobal FFT completed using %d MPI processes\n",
size);
}
/*
Release allocated resources
*/
fftw_destroy_plan(plan);
fftw_free(data);
MPI_Finalize();
return 0;
}
- Compile
ml gcc mpi fftw/3.3.11-intelmpi-2021.16
mpicc fftw_mpi.c -o fftw_mpi -lfftw3_mpi -lfftw3 -lm
- Execute within a SLURM job
srun --ntasks-per-node=4 mpirun -n 4 ./fftw_mpi