Aelian.FFT 1.0.4

dotnet add package Aelian.FFT --version 1.0.4
                    
NuGet\Install-Package Aelian.FFT -Version 1.0.4
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Aelian.FFT" Version="1.0.4" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Aelian.FFT" Version="1.0.4" />
                    
Directory.Packages.props
<PackageReference Include="Aelian.FFT" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Aelian.FFT --version 1.0.4
                    
#r "nuget: Aelian.FFT, 1.0.4"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Aelian.FFT@1.0.4
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Aelian.FFT&version=1.0.4
                    
Install as a Cake Addin
#tool nuget:?package=Aelian.FFT&version=1.0.4
                    
Install as a Cake Tool

Aelian.FFT

GitHub Nuget

A highly optimized fast fourier transform implementation for .NET 8 and up, written in 100% pure c#, so there are no dependencies on native libraries.

It utilizes an in-place iterative Radix-2 Cooley-Tukey algorithm tuned for SIMD, and has support for both complex-valued and real-valued input.

To my knowledge, it is the fastest .NET FFT implementation that is freely and publicly available.

Usage

Initialization

using Aelian.FFT;

// Call Initialize () once when your program is loading
FastFourierTransform.Initialize ();

Transforming complex-valued data

var ComplexBuffer = new System.Numerics.Complex[4096];

// Fill ComplexBuffer with meaningful data here

FastFourierTransform.FFT ( ComplexBuffer, /* forward: */ true );

Transforming real-valued data

var RealBuffer = new double[4096];

// Fill RealBuffer with meaningful data here

FastFourierTransform.RealFFT ( RealBuffer, /* forward: */ true );

Or, alternatively, using the supplied Aelian.FFT.SignalData class to make working with in-place real-valued FFTs easier:

var MySignalData = SignalData.CreateFromRealSize ( 4096 );

// Fill MySignalData with meaningful data here

var RealValuedSamples = MySignalData.AsReal ();
FastFourierTransform.RealFFT ( RealValuedSamples, /* forward: */ true );

var ComplexSpectrum = MySignalData.AsComplex ();

Note that Aelian.FFT utilizes an in-place algorithm, which means your input data is overwritten with output data. This saves costly memory allocation and memory access penalties, but might not always be the most convenient or intuitive approach.

Limitations

Certain optimizations in Aelian.FFT consist of precomputing values in tables. These tables take up some memory and take a short time to initialize. They have a fixed maximum size that limits the maximum input size for the FFT.

The current limit for Constants.MaxTableDepth is 18, which limits the maximum FFT input to 65,536 samples (or 32,768 complex values). Increasing the value of Constants.MaxTableDepth also increases the memory usage exponentially, so while it is certainly possible to process larger FFT's by increasing this value, it is currently set to what I considered to be a reasonable limit.

In the future I might make this value configurable.

Benchmarks comparing Aelian.FFT to other .NET FFT implementations

Benchmarks ran on a 11th Gen Intel Core i9-11900K 3.50GHz, 1 CPU, 16 logical and 8 physical cores, using .NET 8.0.5 (8.0.524.21615), X64 RyuJIT AVX2

The following alternative implementations were used in this benchmark:

Real-valued input

Each benchmark represents running 10,000 transforms with an input of 4096 real values:

Method N Mean Error StdDev Ratio RatioSD
Aelian_RealFFT 4096 122.01 ms 2.079 ms 2.775 ms 1.00 0.00
Aelian_RealFFT_Split * 4096 83.81 ms 0.660 ms 0.551 ms 0.68 0.02
Aelian_RealFFT_Inverse 4096 127.80 ms 1.394 ms 1.164 ms 1.04 0.03
Aelian_RealFFT_Inverse_Split * 4096 86.83 ms 0.973 ms 0.910 ms 0.71 0.02
NWaves_RealFFT_Split 4096 151.71 ms 0.672 ms 0.596 ms 1.23 0.03
NWaves_RealFFT_Inverse_Split 4096 159.52 ms 0.842 ms 0.703 ms 1.29 0.03
Lomont_RealFFT 4096 247.15 ms 4.765 ms 4.224 ms 2.01 0.06
Lomont_RealFFT_Inverse 4096 270.25 ms 1.863 ms 1.555 ms 2.19 0.05
MathNet_RealFFT 4096 1,078.25 ms 5.293 ms 4.692 ms 8.78 0.24
MathNet_RealFFT_Inverse 4096 905.07 ms 5.071 ms 4.235 ms 7.34 0.19
FftSharp_RealFFT 4096 2,801.29 ms 55.419 ms 92.593 ms 23.17 1.09
FftSharp_RealFFT_Inverse 4096 2,878.72 ms 33.555 ms 29.746 ms 23.44 0.75

Note that NAudio does not have a real-valued FFT, so it is omitted from this benchmark.

* The split data overload of RealFFT is faster than the interleaved one, since it can skip unzipping and rezipping, but it is also less practical since it splits out the real-valued samples in even and odd arrays.

Complex-valued input

Each benchmark represents running 10,000 transforms with an input of 4096 complex values:

Method N Mean Error StdDev Median Ratio RatioSD
Aelian_FFT 4096 295.4 ms 2.94 ms 2.29 ms 296.2 ms 1.00 0.00
Aelian_FFT_Split 4096 184.2 ms 0.83 ms 0.77 ms 184.1 ms 0.62 0.01
Aelian_FFT_Inverse 4096 307.5 ms 2.49 ms 2.21 ms 307.4 ms 1.04 0.01
Aelian_FFT_Inverse_Split 4096 192.4 ms 1.19 ms 1.05 ms 192.1 ms 0.65 0.00
NWaves_FFT 4096 560.8 ms 11.08 ms 26.77 ms 568.3 ms 1.90 0.10
NWaves_FFT_Inverse 4096 617.0 ms 13.72 ms 38.26 ms 633.1 ms 2.14 0.03
NAudio_FFT_32 * 4096 501.1 ms 7.82 ms 7.31 ms 500.7 ms 1.70 0.02
NAudio_FFT_Inverse_32 * 4096 194.5 ms 1.71 ms 1.52 ms 194.7 ms 0.66 0.01
Lomont_FFT 4096 584.0 ms 7.60 ms 6.34 ms 583.7 ms 1.97 0.02
Lomont_FFT_Inverse 4096 637.8 ms 11.32 ms 10.58 ms 638.0 ms 2.15 0.04
MathNet_FFT 4096 800.4 ms 6.09 ms 5.40 ms 799.4 ms 2.71 0.03
MathNet_FFT_Inverse 4096 806.9 ms 5.29 ms 4.69 ms 806.2 ms 2.73 0.03
FftSharp_FFT 4096 2,750.8 ms 53.29 ms 49.85 ms 2,735.5 ms 9.28 0.20
FftSharp_FFT_Inverse 4096 2,873.4 ms 47.58 ms 42.18 ms 2,867.0 ms 9.72 0.17

* NAudio only supports its own Complex value type using 32-bit floats, so while it is faster in case of an inverse FFT, its output is also far less precise.

If you know of a .NET FFT implementation that you think belongs in this list, please let me know.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.4 5,999 5/23/2024
1.0.3 128 5/22/2024
1.0.2 116 5/20/2024
1.0.1 106 5/20/2024