Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 27 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@ option(KISSFFT_STATIC "Build kissfft as static (ON) or shared library (OFF)" OFF
option(KISSFFT_TEST "Build kissfft tests" ON)
option(KISSFFT_TOOLS "Build kissfft command-line tools" ON)
option(KISSFFT_USE_ALLOCA "Use alloca instead of malloc" OFF)
option(KISSFFT_USE_SIMDE "Use SIMDE for portable SIMD" OFF)

# Detect x86 architecture
string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" _kf_proc)
if(_kf_proc MATCHES "^(x86_64|amd64|i[3-6]86)$")
set(KISSFFT_X86 TRUE)
else()
set(KISSFFT_X86 FALSE)
endif()

# Auto-enable SIMDE on non-x86 when using simd datatype
if(KISSFFT_DATATYPE STREQUAL "simd" AND NOT KISSFFT_X86)
set(KISSFFT_USE_SIMDE ON)
message(STATUS "Non-x86 detected, auto-enabling SIMDE for SIMD support")
endif()

#
# Validate datatype
Expand Down Expand Up @@ -165,10 +180,14 @@ else()

if(KISSFFT_DATATYPE MATCHES "^simd$")
list(APPEND KISSFFT_COMPILE_DEFINITIONS USE_SIMD)
if (NOT MSVC)
target_compile_options(kissfft PRIVATE -msse)
else()
target_compile_options(kissfft PRIVATE "/arch:SSE")
if(KISSFFT_USE_SIMDE)
list(APPEND KISSFFT_COMPILE_DEFINITIONS USE_SIMD_SIMDE)
elseif(KISSFFT_X86)
if(MSVC)
target_compile_options(kissfft PRIVATE "/arch:SSE")
else()
target_compile_options(kissfft PRIVATE -msse)
endif()
endif()
endif()
endif()
Expand Down Expand Up @@ -325,12 +344,11 @@ if (KISSFFT_PKGCONFIG)
set(PKGCONFIG_KISSFFT_VERSION "${kissfft_VERSION}")
join_paths(PKGCONFIG_KISSFFT_LIBDIR "\${prefix}" "${CMAKE_INSTALL_LIBDIR}")
join_paths(PKGCONFIG_KISSFFT_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
if(KISSFFT_DATATYPE MATCHES "^simd$")
list(APPEND KISSFFT_COMPILE_DEFINITIONS USE_SIMD)
if (NOT MSVC)
set(PKG_KISSFFT_DEFS "${PKG_KISSFFT_DEFS} -msse")
else()
if(KISSFFT_DATATYPE MATCHES "^simd$" AND NOT KISSFFT_USE_SIMDE AND KISSFFT_X86)
if(MSVC)
set(PKG_KISSFFT_DEFS "${PKG_KISSFFT_DEFS} /ARCH:SSE")
else()
set(PKG_KISSFFT_DEFS "${PKG_KISSFFT_DEFS} -msse")
endif()
endif()
if (NOT KISSFFT_OPENMP)
Expand Down
19 changes: 18 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export KISSFFT_OPENMP ?= 0
export KISSFFT_STATIC ?= 0
export KISSFFT_TOOLS ?= 1
export KISSFFT_USE_ALLOCA ?= 0
export KISSFFT_USE_SIMDE ?= 0

#
# Installation directories
Expand Down Expand Up @@ -114,6 +115,17 @@ endif

export KISSFFTLIB_SHORTNAME

# Detect x86 architecture for native SSE
_KISSFFT_ARCH := $(shell uname -m)
_KISSFFT_X86 := $(filter x86_64 i386 i486 i586 i686 amd64,$(_KISSFFT_ARCH))

# Auto-enable SIMDE on non-x86 when using simd datatype
ifeq "$(KISSFFT_DATATYPE)" "simd"
ifeq "$(_KISSFFT_X86)" ""
override KISSFFT_USE_SIMDE = 1
endif
endif

#
# Compile-time definitions by datatype
#
Expand All @@ -127,7 +139,12 @@ ifeq "$(KISSFFT_DATATYPE)" "int32_t"
else ifeq "$(KISSFFT_DATATYPE)" "int16_t"
TYPEFLAGS += -DFIXED_POINT=16
else ifeq "$(KISSFFT_DATATYPE)" "simd"
TYPEFLAGS += -DUSE_SIMD=1 -msse
TYPEFLAGS += -DUSE_SIMD=1
ifeq ($(KISSFFT_USE_SIMDE), 1)
TYPEFLAGS += -DUSE_SIMD_SIMDE
else
TYPEFLAGS += -msse
endif
else ifeq "$(KISSFFT_DATATYPE)" "float"
TYPEFLAGS += -Dkiss_fft_scalar=$(KISSFFT_DATATYPE)
else ifeq "$(KISSFFT_DATATYPE)" "double"
Expand Down
30 changes: 29 additions & 1 deletion README.simd
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,31 @@ This API is not easy to use, is not well documented, and breaks the KISS princip
Still reading? Okay, you may get rewarded for your patience with a considerable speedup
(2-3x) on intel x86 machines with SSE if you are willing to jump through some hoops.

Portable SIMD Support with SIMDE
================================

kissfft now supports SIMDE (SIMD Everywhere) for portable SIMD operations on non-x86
platforms such as ARM, RISC-V, LoongArch, WebAssembly, and others.

To enable SIMDE support:

With CMake:
cmake -DKISSFFT_DATATYPE=simd -DKISSFFT_USE_SIMDE=ON ..

With Makefile:
make KISSFFT_DATATYPE=simd KISSFFT_USE_SIMDE=1

Prerequisites:
- SIMDE headers must be available in your include path
- Install via package manager or include SIMDE as a submodule in your project

When SIMDE is enabled, the library will use SIMDE's portable SSE implementation,
which automatically maps to native SIMD instructions on the target platform or
falls back to scalar operations if no SIMD is available.

Native x86 SSE Support
======================

The basic idea is to use the packed 4 float __m128 data type as a scalar element.
This means that the format is pretty convoluted. It performs 4 FFTs per fft call on signals A,B,C,D.

Expand All @@ -19,9 +44,12 @@ where "rA0" is the real part of the zeroth sample for signal A
Real-only data is laid out:
rA0,rB0,rC0,rD0, rA1,rB1,rC1,rD1, ...

Compile with gcc flags something like
Compile with gcc flags something like (for native x86 SSE):
-O3 -mpreferred-stack-boundary=4 -DUSE_SIMD=1 -msse

Or for portable SIMD with SIMDE:
-O3 -mpreferred-stack-boundary=4 -DUSE_SIMD=1 -DUSE_SIMD_SIMDE

Be aware of SIMD alignment. This is the most likely cause of segfaults.
The code within kissfft uses scratch variables on the stack.
With SIMD, these must have addresses on 16 byte boundaries.
Expand Down
12 changes: 9 additions & 3 deletions _kiss_fft_guts.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,15 @@ struct kiss_fft_state{
# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase))
# define HALF_OF(x) ((x)>>1)
#elif defined(USE_SIMD)
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
# define HALF_OF(x) ((x)*_mm_set1_ps(.5))
# ifdef USE_SIMD_SIMDE
# define KISS_FFT_COS(phase) simde_mm_set1_ps( cos(phase) )
# define KISS_FFT_SIN(phase) simde_mm_set1_ps( sin(phase) )
# define HALF_OF(x) ((x)*simde_mm_set1_ps(.5f))
# else
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
# define HALF_OF(x) ((x)*_mm_set1_ps(.5f))
# endif
#else
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
Expand Down
46 changes: 38 additions & 8 deletions kiss_fft.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,45 @@ extern "C" {

/* User may override KISS_FFT_MALLOC and/or KISS_FFT_FREE. */
#ifdef USE_SIMD
# include <xmmintrin.h>
# define kiss_fft_scalar __m128
# ifndef KISS_FFT_MALLOC
# define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
# define KISS_FFT_ALIGN_CHECK(ptr)
# ifdef USE_SIMD_SIMDE
# include <simde/x86/sse.h>
# define kiss_fft_scalar simde__m128
# define KISS_FFT_SET1_PS(x) simde_mm_set1_ps(x)
# ifndef KISS_FFT_MALLOC
# include <stdlib.h>
# if defined(_WIN32)
# define KISS_FFT_MALLOC(nbytes) _aligned_malloc(nbytes,16)
# define KISS_FFT_FREE _aligned_free
# elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || \
(defined(__cplusplus) && __cplusplus >= 201703L)
/* C11 aligned_alloc or C++17 */
# define KISS_FFT_MALLOC(nbytes) aligned_alloc(16, ((nbytes) + 15) & ~(size_t)15)
# define KISS_FFT_FREE free
# else
/* POSIX posix_memalign */
static inline void* kiss_fft_simde_malloc(size_t nbytes) {
void* ptr = NULL;
if (posix_memalign(&ptr, 16, nbytes) != 0) return NULL;
return ptr;
}
# define KISS_FFT_MALLOC(nbytes) kiss_fft_simde_malloc(nbytes)
# define KISS_FFT_FREE free
# endif
# endif
# define KISS_FFT_ALIGN_CHECK(ptr)
# define KISS_FFT_ALIGN_SIZE_UP(size) ((size + 15UL) & ~0xFUL)
# endif
# ifndef KISS_FFT_FREE
# define KISS_FFT_FREE _mm_free
# else
# include <xmmintrin.h>
# define kiss_fft_scalar __m128
# define KISS_FFT_SET1_PS(x) _mm_set1_ps(x)
# ifndef KISS_FFT_MALLOC
# define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
# define KISS_FFT_ALIGN_CHECK(ptr)
# define KISS_FFT_ALIGN_SIZE_UP(size) ((size + 15UL) & ~0xFUL)
# endif
# ifndef KISS_FFT_FREE
# define KISS_FFT_FREE _mm_free
# endif
# endif
#else
# define KISS_FFT_ALIGN_CHECK(ptr)
Expand Down
4 changes: 2 additions & 2 deletions kiss_fftr.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *fr
freqdata[0].r = tdc.r + tdc.i;
freqdata[ncfft].r = tdc.r - tdc.i;
#ifdef USE_SIMD
freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
freqdata[ncfft].i = freqdata[0].i = KISS_FFT_SET1_PS(0);
#else
freqdata[ncfft].i = freqdata[0].i = 0;
#endif
Expand Down Expand Up @@ -146,7 +146,7 @@ void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *t
C_ADD (st->tmpbuf[k], fek, fok);
C_SUB (st->tmpbuf[ncfft - k], fek, fok);
#ifdef USE_SIMD
st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
st->tmpbuf[ncfft - k].i *= KISS_FFT_SET1_PS(-1.0);
#else
st->tmpbuf[ncfft - k].i *= -1;
#endif
Expand Down
10 changes: 6 additions & 4 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,12 @@ add_kissfft_test_executable(testcpp testcpp.cc)
if(KISSFFT_DATATYPE MATCHES "^simd$")
add_kissfft_test_executable(tsimd test_simd.c)
target_compile_definitions(tsimd PRIVATE USE_SIMD)
if (NOT MSVC)
target_compile_options(kissfft PRIVATE -msse)
else()
target_compile_options(kissfft PRIVATE "/arch:SSE")
if(NOT KISSFFT_USE_SIMDE AND KISSFFT_X86)
if (NOT MSVC)
target_compile_options(tsimd PRIVATE -msse)
else()
target_compile_options(tsimd PRIVATE "/arch:SSE")
endif()
endif()
endif()

Expand Down
2 changes: 1 addition & 1 deletion test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ endif

$(TESTSIMD): test_simd.c
ifeq "$(KISSFFT_DATATYPE)" "simd"
$(CC) -o $@ -g $(CFLAGS) -DUSE_SIMD=1 -msse $< -L.. -l$(KISSFFTLIB_SHORTNAME) -lm
$(CC) -o $@ -g $(CFLAGS) $(TYPEFLAGS) $< -L.. -l$(KISSFFTLIB_SHORTNAME) -lm
else
$(error ERROR: This test makes sense only with KISSFFT_DATATYPE=simd)
endif
Expand Down
2 changes: 1 addition & 1 deletion test/test_real.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ static
kiss_fft_scalar rand_scalar(void)
{
#ifdef USE_SIMD
return _mm_set1_ps(rand()-RAND_MAX/2);
return KISS_FFT_SET1_PS(rand()-RAND_MAX/2);
#else
kiss_fft_scalar s = (kiss_fft_scalar)(rand() -RAND_MAX/2);
return s/2;
Expand Down
8 changes: 4 additions & 4 deletions test/test_simd.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ static void test1(void)
int n[2] = {256,256};
size_t nbytes = sizeof(kiss_fft_cpx)*n[0]*n[1];

kiss_fft_cpx * inbuf = _mm_malloc(nbytes,16);
kiss_fft_cpx * outbuf = _mm_malloc(nbytes,16);
kiss_fft_cpx * inbuf = KISS_FFT_MALLOC(nbytes);
kiss_fft_cpx * outbuf = KISS_FFT_MALLOC(nbytes);
memset(inbuf,0,nbytes);
memset(outbuf,0,nbytes);

kiss_fftnd_cfg cfg = kiss_fftnd_alloc(n,2,is_inverse,0,0);
kiss_fftnd(cfg,inbuf,outbuf);
kiss_fft_free(cfg);
_mm_free(inbuf);
_mm_free(outbuf);
KISS_FFT_FREE(inbuf);
KISS_FFT_FREE(outbuf);
}

int main(void)
Expand Down
2 changes: 1 addition & 1 deletion test/twotonetest.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ double two_tone_test( int nfft, int bin1,int bin2)
/* generate a signal with two tones*/
for (i = 0; i < nfft; i++) {
#ifdef USE_SIMD
tbuf[i] = _mm_set1_ps( (maxrange>>1)*cos(f1*i)
tbuf[i] = KISS_FFT_SET1_PS( (maxrange>>1)*cos(f1*i)
+ (maxrange>>1)*cos(f2*i) );
#else
tbuf[i] = (maxrange>>1)*cos(f1*i)
Expand Down
8 changes: 4 additions & 4 deletions tools/kiss_fastfir.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ kiss_fastfir_cfg kiss_fastfir_alloc(

for ( i=0; i < st->n_freq_bins; ++i ) {
#ifdef USE_SIMD
st->fir_freq_resp[i].r *= _mm_set1_ps(scale);
st->fir_freq_resp[i].i *= _mm_set1_ps(scale);
st->fir_freq_resp[i].r *= KISS_FFT_SET1_PS(scale);
st->fir_freq_resp[i].i *= KISS_FFT_SET1_PS(scale);
#else
st->fir_freq_resp[i].r *= scale;
st->fir_freq_resp[i].i *= scale;
Expand Down Expand Up @@ -286,7 +286,7 @@ void direct_file_filter(
tmph = imp_resp+nlag;
#ifdef REAL_FASTFIR
# ifdef USE_SIMD
outval = _mm_set1_ps(0);
outval = KISS_FFT_SET1_PS(0);
#else
outval = 0;
#endif
Expand All @@ -297,7 +297,7 @@ void direct_file_filter(
outval += buf[k] * *tmph;
#else
# ifdef USE_SIMD
outval.r = outval.i = _mm_set1_ps(0);
outval.r = outval.i = KISS_FFT_SET1_PS(0);
#else
outval.r = outval.i = 0;
#endif
Expand Down