rustfft

repository·master·Indexed 21 days ago

https://github.com/ejmahler/rustfft

A high-performance, SIMD-accelerated FFT library written in pure Rust. It supports arbitrary FFT sizes with O(nlogn) complexity and provides automatic algorithm selection via FftPlanner. The library includes support for AVX, SSE4.1, NEON, and WASM SIMD acceleration through specific feature flags and planners. It requires rustc 1.61 or newer and provides in-place and out-of-place computation methods.

Tokens
5K
Snippets
14
Records
21
Agent score
70%

What's inside rustfft

  1. How SIMD acceleration works in RustFFT

    master

    RustFFT uses SIMD (Single Instruction, Multiple Data) to accelerate FFT computations. The behavior depends on the target architecture:

    • x86_64: Automatically detects and uses AVX (if avx and fma are supported) or SSE4.1. No special code is required; FftPlanner handles the selection.
    • AArch64: Automatically detects and uses NEON instructions via FftPlanner.
    • WebAssembly: Does not support dynamic feature detection. To use WASM SIMD, you must explicitly enable the wasm_simd feature flag. Note that running SIMD-enabled WASM on a runtime that does not support fixed-width SIMD will cause a trap (immediate execution cancellation).
  2. Migrate FFT Direction from boolean to FftDirection enum

    master

    In RustFFT 5.0, the boolean parameter used for direction in 4.0 has been replaced by the FftDirection enum.

    Algorithm Constructors

    When constructing algorithms like Radix4, replace false (forward) and true (inverse) with FftDirection::Forward and FftDirection::Inverse respectively.

    FftPlanner Changes

    In 4.0, FFTplanner was initialized with a direction. In 5.0, FftPlanner::new() takes no arguments, and the direction is specified during the planning phase using plan_fft or convenience methods.

    Trait Method Changes

    • The IsInverse trait is now Direction.
    • The method is_inverse() is now fft_direction() -> FftDirection.
    // RustFFT 4.0
    let fft_forward = Radix4::new(4096, false);
    let fft_inverse = Radix4::new(4096, true);
    
    // RustFFT 5.0
    let fft_forward = Radix4::new(4096, FftDirection::Forward);
    let fft_inverse = Radix4::new(4096, FftDirection::Inverse);
    // RustFFT 4.0
    let planner_forward = FFTplanner::new(false);
    let fft_forward = planner.plan_fft(1234);
    
    let planner_inverse = FFTplanner::new(true);
    let fft_inverse = planner.plan_fft(1234);
    
    // RustFFT 5.0
    let planner = FftPlanner::new();
    
    let fft_forward1 = planner.plan_fft(1234, FftDirection::Forward);
    let fft_forward2 = planner.plan_fft_forward(1234);
    
    let fft_inverse1 = planner.plan_fft(1234, FftDirection::Inverse);
    let fft_inverse2 = planner.plan_fft_inverse(1234);
  3. Migrate from RustFFT 4.0 to 5.0: Renamed Structs and Traits

    master

    To comply with Rust API guidelines for acronyms, several core types were renamed in version 5.0. Update your imports and type references as follows:

    • FFT $\rightarrow$ Fft (trait)
    • FFTnum $\rightarrow$ FftNum (trait)
    • FFTplanner $\rightarrow$ FftPlanner (struct)
    • DFT $\rightarrow$ Dft (struct)
  4. How to normalize FFT outputs

    master

    RustFFT does not perform normalization. Callers are responsible for manually scaling the results.

    To normalize a single FFT, scale each element by 1/len().sqrt().

    If you are performing a forward FFT followed by an inverse FFT, you can combine the normalization steps into a single step by scaling each element by 1/len().

  5. Optimize FFT performance via input size

    master

    FFT computation speed depends heavily on the prime factorization of the size $N$.

    • Fastest: Sizes of the form $2^n \times 3^m$ (e.g., power-of-two).
    • Very Fast: Sizes where all prime factors are $\le 11$.
    • Noticeably Slower: Sizes with larger prime factors or prime numbers.

    If your application allows choosing the FFT size, aim for sizes whose prime factors are small (ideally 2 and 3) to achieve significant speedups.

  6. Use FftPlanner to create FFT implementations

    master

    The FftPlanner is the primary interface for obtaining FFT algorithm instances. When you create a new FftPlanner, it automatically detects available CPU features (like AVX, SSE, Neon, or WASM SIMD) and selects the fastest available instruction set for planning.

    Best Practices:

    • Reuse the planner: If you need to create multiple FFT instances, reuse the same FftPlanner instance. The planner re-uses internal data across calls to reduce memory usage and initialization time. FFT instances created with different planners cannot share data.
    • Safe to drop: Each Fft instance returned by the planner owns its internal data via Arcs, so it is safe to drop the planner once the FFT instances have been created.
    // Perform a forward Fft of size 1234
    use std::sync::Arc;
    use rustfft::{FftPlanner, num_complex::Complex};
    
    let mut planner = FftPlanner::new();
    let fft = planner.plan_fft_forward(1234);
    
    let mut buffer = vec![Complex{ re: 0.0f32, im: 0.0f32 }; 1234];
    fft.process(&mut buffer);
    
    // The FFT instance returned by the planner has the type `Arc<dyn Fft<T>>`,
    // where T is the numeric type, ie f32 or f64, so it's cheap to clone
    let fft_clone = Arc::clone(&fft);
  7. Perform an FFT using FftPlanner

    master

    The recommended way to use RustFFT is to create an FftPlanner instance and use its planning methods. This automatically selects the best algorithm for a given size and handles precomputed data. The planner returns trait objects of the Fft trait, which allows for FFT sizes that are determined at runtime.

    // Perform a forward FFT of size 1234
    use rustfft::{FftPlanner, num_complex::Complex};
    
    let mut planner = FftPlanner::new();
    let fft = planner.plan_fft_forward(1234);
    
    let mut buffer = vec![Complex{ re: 0.0f32, im: 0.0f32 }; 1234];
    fft.process(&mut buffer);
  8. Perform a forward FFT with RustFFT

    master

    To compute an FFT, use the FftPlanner to create an FFT instance for a specific size and direction. The planner automatically selects the most efficient algorithm for the given size. You then pass a mutable buffer of Complex numbers to the process method to perform the computation in-place.

    // Perform a forward FFT of size 1234
    use rustfft::{FftPlanner, num_complex::Complex};
    
    let mut planner = FftPlanner::<f32>::new();
    let fft = planner.plan_fft_forward(1234);
    
    let mut buffer = vec![Complex{ re: 0.0, im: 0.0 }; 1234];
    
    fft.process(&mut buffer);
  9. Migrate from FFTButterfly to Fft trait

    master

    The FFTbutterfly trait has been deleted in RustFFT 5.0. Its functionality has been merged into the Fft trait. Additionally, algorithms that previously required FFTbutterfly objects have been renamed and now accept Fft objects:

    • MixedRadixDoubleButterfly $\rightarrow$ MixedRadixSmall
    • GoodThomasAlgorithmDoubleButterfly $\rightarrow$ GoodThomasAlgorithmSmall

    Methods like process_inplace and process_multi_inplace are now part of the Fft trait (e.g., Fft::process_inplace or Fft::process_inplace_multi).

    // RustFFT 4.0
    let butterfly8 : Arc<dyn FFTbutterfly<T>> = Arc::new(Butterfly8::new(false));
    let butterfly3 : Arc<dyn FFTbutterfly<T>> = Arc::new(Butterfly3::new(false));
    
    let fft1 = MixedRadixDoubleButterfly::new(Arc::clone(&butterfly8), Arc::clone(&butterfly3));
    let fft2 = GoodThomasAlgorithmDoubleButterfly::new(Arc::clone(&butterfly8), Arc::clone(&butterfly3));
    
    // RustFFT 5.0
    let butterfly8 : Arc<dyn Fft<T>> = Arc::new(Butterfly8::new(FftDirection::Forward));
    let butterfly3 : Arc<dyn Fft<T>> = Arc::new(Butterfly3::new(FftDirection::Forward));
    
    let fft1 = MixedRadixSmall::new(Arc::clone(&butterfly8), Arc::clone(&butterfly3));
    let fft2 = GoodThomasAlgorithmSmall::new(Arc::clone(&butterfly8), Arc::clone(&butterfly3));
  10. Use the new Fft trait methods (In-place vs Out-of-place)

    master

    In RustFFT 5.0, the distinction between single and multi-FFT methods is removed; all methods automatically handle multiple FFTs if the buffer length is a multiple of the FFT length. The Fft trait now provides three primary ways to process data:

    1. Fft::process(&mut buffer): Performs in-place computation. It allocates scratch space internally as needed.
    2. Fft::process_with_scratch(&mut data, &mut scratch): Performs in-place computation using a provided scratch buffer.
    3. Fft::process_outofplace_with_scratch(&mut input, &mut output, &mut scratch): Performs out-of-place computation, reading from input and writing to output using the provided scratch buffer.

    To use the out-of-place method, you must provide a scratch buffer of length fft.get_outofplace_scratch_len().

    // RustFFT 4.0 (Old out-of-place style)
    let fft = Radix4::new(4096, false);
    let mut input : Vec<Complex<f32>> = get_my_input_data();
    let mut output = vec![Complex::zero(); fft.len()];
    fft.process(&mut input, &mut output);
    
    // RustFFT 5.0 (New in-place style)
    let fft = Radix4::new(4096, FftDirection::Forward);
    let mut buffer : Vec<Complex<f32>> = get_my_input_data();
    fft.process(&mut buffer);
    
    // RustFFT 5.0 (New out-of-place style)
    let fft = Radix4::new(4096, FftDirection::Forward);
    let mut input : Vec<Complex<f32>> = get_my_input_data();
    let mut output = vec![Complex::zero(); fft.len()];
    let mut scratch = vec![Complex::zero(); fft.get_outofplace_scratch_len()];
    fft.process_outofplace_with_scratch(&mut input, &mut output, &mut scratch);
  11. Migrate Rader's Algorithm Constructor

    master

    In RustFFT 5.0, the RadersAlgorithm::new constructor no longer requires the len: usize parameter. It now automatically derives the FFT length from the provided inner FFT instance.

    // RustFFT 4.0
    let inner_fft : Arc<dyn Fft<T>> = ...;
    let fft = RadersAlgorithm::new(inner_fft.len() + 1, inner_fft);
    
    // RustFFT 5.0
    let inner_fft : Arc<dyn Fft<T>> = ...;
    let fft = RadersAlgorithm::new(inner_fft);
  12. RustFFT requirements and stability

    master

    Minimum Supported Rust Version (MSRV)

    RustFFT requires rustc 1.61 or newer.

    Stability Policy

    The project commits to making no breaking API changes for 3-year intervals. The only exception is when the re-exported num-complex or num-traits crates release major version bumps, which will trigger a major version change in RustFFT to maintain ecosystem compatibility.