nk 1.0.0
C++ Numerical Computing Runtime
Loading...
Searching...
No Matches
fft_kernels.hpp
1// ops/include/nk/ops/fft/fft_kernels.hpp
2//
3// Private kernel-dispatch header for FFT backends. Compiled by
4// exactly one of backends/MDspFft_{portable,simd}.cpp based on
5// NK_WITH_SIMD. The only caller of fftRadix2Impl is fftAlongDim
6// in fft.cpp — other FFT users (transforms/, spectral_analysis/,
7// convFFT) still call the inline scalar fftRadix2 in helpers.hpp
8// directly; migrate them individually if their perf matters.
9
10#pragma once
11
12#include <nk/value/value.hpp> // Complex (nk::Complex) // Complex
13
14#include <cstddef>
15
16namespace nk::ops::detail {
17
18// In-place radix-2 FFT over an interleaved-complex buffer.
19// N must be a power of 2. W is a precomputed twiddle table of
20// length N/2 (see fillFftTwiddles in helpers.hpp).
21//
22// The portable backend forwards to the inline scalar reference.
23// The SIMD backend converts to SoA (split real/imag) internally,
24// runs vectorised butterflies via Highway, and converts back.
25void fftRadix2Impl(Complex *buf, std::size_t N, const Complex *W);
26
27// Stockham auto-sort radix-2 dispatcher (SIMD backend only). Same
28// contract as fftRadix2Impl but uses an internal thread-local
29// scratch buffer instead of working in place — eliminates the
30// bit-reversal pre-pass at the cost of 2× memory for the working
31// state. Exposed for benchmarking from fft_bench; the public
32// fftRadix2Impl chooses whether to route here based on N.
33void fftStockhamDispatch(Complex *buf, std::size_t N, const Complex *W);
34
35// SoA radix-2 dispatcher: same in-place semantics as fftRadix2Impl,
36// but the kernel converts buf → split real/imag arrays internally to
37// avoid the AVX2 LoadInterleaved2/StoreInterleaved2 permute cost,
38// then converts back. Per-thread re/im scratch grows monotonically.
39void fftRadix2SoaDispatch(Complex *buf, std::size_t N, const Complex *W);
40
41// SoA radix-4 dispatcher: same algorithm as the AoS r4 path, but
42// using split real/imag layout to skip the AVX2 LoadInterleaved2 ×4
43// + StoreInterleaved2 ×4 permute cost per inner iteration. N must
44// be a power of 4. Per-thread re/im scratch grows monotonically.
45void fftRadix4Pow4SoaDispatch(Complex *buf, std::size_t N, const Complex *W);
46
47// SoA-native FFT stages: caller provides split-real/imag scratch
48// already populated with the input. Runs bit-reverse + butterfly
49// stages in place on re/im, picks r2 or r4 internally based on N.
50// Used by the wrapper's rfft path which packs straight into SoA and
51// twists straight from SoA, avoiding the per-call AoS↔SoA conversion
52// the public AoS-in/out dispatchers wrap around the same kernels.
53//
54// On WASM (Emscripten target) — the dispatcher falls back to AoS in
55// the wrapper since SoA doesn't help SIMD128. This entry point still
56// works (it just routes to the AoS path with internal conversion);
57// the wrapper checks the platform and decides.
58void fftSoaStagesDispatch(double *re, double *im, std::size_t N, const Complex *W);
59
60} // namespace nk::ops::detail