Table of Contents

  1. Finding FFTW
  2. Single, Double & Long Precision
  3. Threading Support
  4. Writing a FindFFTW Module
  5. Building FFTW from Source
  6. FFTW3 vs FFTW2 Migration
Back to CMake Mastery Series

FFTW

June 4, 2026 Wasil Zafar 8 min read

Integrate FFTW — the Fastest Fourier Transform in the West — with CMake for high-performance signal processing, spectral analysis, and scientific computing.

Math

Finding FFTW

FFTW does not ship CMake config files by default — you need either a custom find module or pkg-config integration. CMake has no built-in FindFFTW.cmake, so projects typically provide their own:

# Using pkg-config to find FFTW (simplest approach)
cmake_minimum_required(VERSION 3.20)
project(SignalApp LANGUAGES C CXX)

find_package(PkgConfig REQUIRED)
pkg_check_modules(FFTW3 REQUIRED IMPORTED_TARGET fftw3)

add_executable(signal_app main.cpp)
target_link_libraries(signal_app PRIVATE PkgConfig::FFTW3)

For systems without pkg-config (Windows), or for more control, use a manual search:

# Manual FFTW discovery
cmake_minimum_required(VERSION 3.20)
project(FFTApp LANGUAGES CXX)

# Search for FFTW header and libraries
find_path(FFTW3_INCLUDE_DIR fftw3.h
    HINTS
        ${FFTW3_ROOT}/include
        /usr/local/include
        /opt/fftw/include
)

find_library(FFTW3_LIBRARY fftw3
    HINTS
        ${FFTW3_ROOT}/lib
        /usr/local/lib
        /opt/fftw/lib
)

# Create imported target
if(FFTW3_INCLUDE_DIR AND FFTW3_LIBRARY)
    add_library(FFTW3::fftw3 IMPORTED INTERFACE)
    set_target_properties(FFTW3::fftw3 PROPERTIES
        INTERFACE_INCLUDE_DIRECTORIES "${FFTW3_INCLUDE_DIR}"
        INTERFACE_LINK_LIBRARIES "${FFTW3_LIBRARY}"
    )
endif()

add_executable(fft_app main.cpp)
target_link_libraries(fft_app PRIVATE FFTW3::fftw3)
Key Insight: Some Linux distributions (Debian/Ubuntu) package FFTW3's CMake config files in libfftw3-dev. On these systems, find_package(FFTW3 CONFIG) may work directly without a custom find module.

Single, Double & Long Precision

FFTW3 provides separate libraries for each floating-point precision. The API is identical but prefixed differently:

# Finding all FFTW precision variants
find_library(FFTW3_DOUBLE_LIB fftw3)        # double precision (default)
find_library(FFTW3_SINGLE_LIB fftw3f)       # float (single precision)
find_library(FFTW3_LONG_LIB fftw3l)         # long double precision
find_library(FFTW3_QUAD_LIB fftw3q)         # __float128 (GCC only)

add_executable(multi_precision main.cpp)
target_link_libraries(multi_precision PRIVATE
    ${FFTW3_DOUBLE_LIB}
    ${FFTW3_SINGLE_LIB}
)
target_include_directories(multi_precision PRIVATE ${FFTW3_INCLUDE_DIR})
// main.cpp — FFTW3 multi-precision usage
#include <fftw3.h>
#include <cmath>
#include <iostream>
#include <vector>

int main() {
    const int N = 1024;

    // Double precision FFT
    std::vector<double> in_d(N);
    for (int i = 0; i < N; i++)
        in_d[i] = std::sin(2.0 * M_PI * 50.0 * i / N);

    fftw_complex* out_d = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * (N/2+1));
    fftw_plan plan_d = fftw_plan_dft_r2c_1d(N, in_d.data(), out_d, FFTW_ESTIMATE);
    fftw_execute(plan_d);

    std::cout << "Double: DC component = " << out_d[0][0] << "\n";

    fftw_destroy_plan(plan_d);
    fftw_free(out_d);

    // Single precision FFT (fftwf_ prefix)
    std::vector<float> in_f(N);
    for (int i = 0; i < N; i++)
        in_f[i] = std::sin(2.0f * (float)M_PI * 50.0f * i / N);

    fftwf_complex* out_f = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * (N/2+1));
    fftwf_plan plan_f = fftwf_plan_dft_r2c_1d(N, in_f.data(), out_f, FFTW_ESTIMATE);
    fftwf_execute(plan_f);

    std::cout << "Single: DC component = " << out_f[0][0] << "\n";

    fftwf_destroy_plan(plan_f);
    fftwf_free(out_f);

    return 0;
}

Threading Support

FFTW provides parallel execution via pthreads or OpenMP. Each requires linking the corresponding thread library:

# FFTW with threading (pthreads)
find_library(FFTW3_THREADS_LIB fftw3_threads)
find_package(Threads REQUIRED)

add_executable(fft_threaded threaded.cpp)
target_link_libraries(fft_threaded PRIVATE
    ${FFTW3_LIBRARY}
    ${FFTW3_THREADS_LIB}
    Threads::Threads
    m  # math library on Linux
)

# Alternative: FFTW with OpenMP
find_library(FFTW3_OMP_LIB fftw3_omp)
find_package(OpenMP REQUIRED)

add_executable(fft_omp omp_fft.cpp)
target_link_libraries(fft_omp PRIVATE
    ${FFTW3_LIBRARY}
    ${FFTW3_OMP_LIB}
    OpenMP::OpenMP_CXX
)
// threaded.cpp — Multi-threaded FFTW
#include <fftw3.h>
#include <iostream>
#include <vector>
#include <cmath>

int main() {
    // Initialize threading (call once before any plan creation)
    fftw_init_threads();
    fftw_plan_with_nthreads(4);  // Use 4 threads

    const int N = 1048576;  // 1M points — threading helps here
    std::vector<double> signal(N);
    for (int i = 0; i < N; i++)
        signal[i] = std::sin(2.0 * M_PI * 440.0 * i / 44100.0);

    fftw_complex* spectrum = (fftw_complex*)fftw_malloc(
        sizeof(fftw_complex) * (N/2 + 1));

    fftw_plan plan = fftw_plan_dft_r2c_1d(N, signal.data(), spectrum, FFTW_MEASURE);
    fftw_execute(plan);

    std::cout << "Computed " << N << "-point FFT with 4 threads\n";

    fftw_destroy_plan(plan);
    fftw_free(spectrum);
    fftw_cleanup_threads();

    return 0;
}
Pitfall — Thread Safety: FFTW plan creation is NOT thread-safe — you must create plans sequentially. However, fftw_execute() is safe to call concurrently with different plans. Always initialize threads before creating the first plan.

Writing a FindFFTW Module

A reusable FindFFTW.cmake module that handles all precision variants and threading:

# cmake/FindFFTW.cmake — Reusable find module
# Usage: find_package(FFTW REQUIRED COMPONENTS DOUBLE FLOAT THREADS)

include(FindPackageHandleStandardArgs)

# Find include directory
find_path(FFTW_INCLUDE_DIR fftw3.h
    HINTS ${FFTW_ROOT} ENV FFTW_ROOT
    PATH_SUFFIXES include
)

# Component library map
set(_fftw_component_libs
    DOUBLE  fftw3
    FLOAT   fftw3f
    LONG    fftw3l
    THREADS fftw3_threads
    FLOAT_THREADS fftw3f_threads
    OMP     fftw3_omp
)

# Find requested component libraries
set(FFTW_LIBRARIES "")
foreach(_comp ${FFTW_FIND_COMPONENTS})
    list(FIND _fftw_component_libs ${_comp} _idx)
    if(_idx GREATER_EQUAL 0)
        math(EXPR _lib_idx "${_idx} + 1")
        list(GET _fftw_component_libs ${_lib_idx} _lib_name)

        find_library(FFTW_${_comp}_LIBRARY ${_lib_name}
            HINTS ${FFTW_ROOT} ENV FFTW_ROOT
            PATH_SUFFIXES lib lib64
        )

        if(FFTW_${_comp}_LIBRARY)
            list(APPEND FFTW_LIBRARIES ${FFTW_${_comp}_LIBRARY})
            set(FFTW_${_comp}_FOUND TRUE)
        endif()
    endif()
endforeach()

find_package_handle_standard_args(FFTW
    REQUIRED_VARS FFTW_INCLUDE_DIR FFTW_LIBRARIES
    HANDLE_COMPONENTS
)

# Create imported targets
if(FFTW_FOUND)
    if(NOT TARGET FFTW::FFTW)
        add_library(FFTW::FFTW INTERFACE IMPORTED)
        set_target_properties(FFTW::FFTW PROPERTIES
            INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIR}"
            INTERFACE_LINK_LIBRARIES "${FFTW_LIBRARIES}"
        )
    endif()
endif()
# Using the custom find module in your project
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")

find_package(FFTW REQUIRED COMPONENTS DOUBLE FLOAT THREADS)

add_executable(spectral_app main.cpp)
target_link_libraries(spectral_app PRIVATE FFTW::FFTW)

Building FFTW from Source

FFTW supports CMake as a build system (since FFTW 3.3.5), making source builds straightforward:

# Build FFTW3 from source with CMake
git clone https://github.com/FFTW/fftw3.git
cd fftw3
git checkout fftw-3.3.10

# Double precision build
cmake -B build-double -G Ninja \
    -DCMAKE_INSTALL_PREFIX=/opt/fftw3 \
    -DCMAKE_BUILD_TYPE=Release \
    -DENABLE_THREADS=ON \
    -DENABLE_OPENMP=ON \
    -DENABLE_AVX2=ON \
    -DENABLE_SSE2=ON \
    -DBUILD_SHARED_LIBS=ON \
    -DBUILD_TESTS=OFF

cmake --build build-double --parallel 8
cmake --install build-double

# Single precision build (must be separate!)
cmake -B build-single -G Ninja \
    -DCMAKE_INSTALL_PREFIX=/opt/fftw3 \
    -DCMAKE_BUILD_TYPE=Release \
    -DENABLE_FLOAT=ON \
    -DENABLE_THREADS=ON \
    -DENABLE_AVX2=ON \
    -DENABLE_SSE2=ON \
    -DBUILD_SHARED_LIBS=ON

cmake --build build-single --parallel 8
cmake --install build-single
Separate Builds: FFTW requires separate CMake builds for each precision level (double, single, long double). Each produces different library names (libfftw3, libfftw3f, libfftw3l) but can be installed to the same prefix.

FFTW3 vs FFTW2 Migration

FFTW2 (legacy) and FFTW3 have incompatible APIs. If migrating from FFTW2:

# Detect FFTW version and adapt
find_path(FFTW_INCLUDE_DIR fftw3.h)

if(FFTW_INCLUDE_DIR)
    message(STATUS "Found FFTW3 headers")
    set(FFTW_VERSION 3)
else()
    find_path(FFTW_INCLUDE_DIR fftw.h)  # FFTW2 header
    if(FFTW_INCLUDE_DIR)
        message(WARNING "Found FFTW2 — consider upgrading to FFTW3")
        set(FFTW_VERSION 2)
    endif()
endif()

# Version-conditional compilation
target_compile_definitions(app PRIVATE FFTW_VERSION=${FFTW_VERSION})
// Abstraction layer for FFTW2/FFTW3 migration
#if FFTW_VERSION == 3
#include <fftw3.h>
#define FFT_PLAN fftw_plan
#define FFT_COMPLEX fftw_complex
#define FFT_MALLOC(n) fftw_malloc(n)
#define FFT_FREE(p) fftw_free(p)
#define FFT_DESTROY_PLAN(p) fftw_destroy_plan(p)
#define FFT_EXECUTE(p) fftw_execute(p)
#else
// FFTW2 compatibility (deprecated)
#include <fftw.h>
#define FFT_PLAN fftw_plan
#define FFT_COMPLEX fftw_complex
#define FFT_MALLOC(n) malloc(n)
#define FFT_FREE(p) free(p)
#define FFT_DESTROY_PLAN(p) fftw_destroy_plan(p)
#define FFT_EXECUTE(p) /* FFTW2 uses fftw_one() */
#endif
Best Practices Summary:
  • Use pkg-config on Linux/macOS for the simplest FFTW discovery
  • Write a custom FindFFTW.cmake for cross-platform portability
  • Always link precision-specific libraries (fftw3f for float)
  • Initialize threads before creating plans (fftw_init_threads())
  • Build each precision variant separately when compiling from source
  • Enable FFTW_MEASURE for production — FFTW_ESTIMATE for development