RustFFT: any-size FFT in pure Rust with automatic SIMD dispatch
RustFFT is a high-performance FFT library written in pure Rust.
At a glance
- What is it?
- RustFFT computes FFTs of any length, including prime sizes, in O(nlogn) time, picks AVX, SSE4.1 or NEON code paths at runtime, and ships under MIT or Apache-2.0 with an MSRV of rustc 1.86.
- Who is it for?
- RustFFT is the right fit for a Rust codebase that needs FFTs of unusual sizes, in particular prime or mixed-radix lengths where a power-of-two-only library would pad the input and change the result. It is the wrong choice if you need a hard guarantee about the instruction set used, or if you are on WebAssembly and cannot guarantee a runtime with fixed-width SIMD, since the wasm_simd path is unconditional and traps on an unsupported runtime.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.
Star history
- ejmahler/RustFFT962 ★ · +13
| Repository | 2026-10-21 | 2026-11-07 |
|---|---|---|
| ejmahler/RustFFT | 949 | 962 |
13 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
RustFFT handles transform sizes that are not powers of two
Most FFT libraries assume a power-of-two length and everything else is a special case. RustFFT takes the opposite position. Its stated capability is that it can compute FFTs of any size, including prime-number sizes, in O(nlogn) time. That single line explains the library's appeal for anyone working outside the comfortable case of a 4096-point radix-2 transform.
The project describes itself as a high-performance, SIMD-accelerated FFT library written in pure Rust, and the primary language is Rust with no C dependency in sight. For a Rust developer, the practical difference is supply-chain and build simplicity: adding a transform does not mean adding a native library, a system package, or a cross-compilation headache for every target you ship to.
The audience is narrow but real. It is people writing signal processing, audio, or scientific code in Rust who have outgrown a naive transform. The naive option, computing the discrete Fourier transform directly, costs O(n^2) work per transform. RustFFT's O(nlogn) algorithms change the practical cost of a large transform by orders of magnitude, and the fact that prime lengths are first-class rather than padded means you do not have to change the mathematics of the problem to fit the library's structure.
The crate is published on crates.io as `rustfft`, with API documentation hosted on docs.rs. The package metadata lists the description "High-performance FFT library written in pure Rust.", the edition 2021 and the categories for algorithms, compression, multimedia encoding and science.
Planning picks an algorithm, then process() works on your buffer
The usage model has two steps: plan a transform, then run it over a buffer. The README's example is short enough to hold in your head.
// 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);Two details carry the design. First, the size 1234 in the example is deliberately not a power of two, which tells you the planner is the component that understands mixed-radix and prime factorisation. Second, the transform operates in place on a `Vec<Complex<f32>>` that you allocate and fill, rather than returning a new vector. The planner is the expensive part, so the intended usage is to plan once and reuse the plan across many buffers.
The element type is a parameter. The example uses `FftPlanner::<f32>::new()`, and the complex type comes from the re-exported `num_complex` crate rather than from a separate import, so you do not have to match version numbers between your own `num-complex` dependency and the one RustFFT uses internally. The repository carries an `examples/` directory with `asmtest.rs` and `concurrency.rs`, and a `benches/` directory, which indicates that the transform is safe to drive from several threads at once, although the README does not spell out the concurrency contract.
AVX, SSE4.1 and NEON are chosen at plan time, not by flags
The dispatch story is the part of RustFFT that surprises people coming from C libraries. On x86_64, the library supports the AVX instruction set, and the documentation is emphatic that no special code is needed to activate it: plan a transform with the `FftPlanner` on a machine that supports the `avx` and `fma` CPU features, and RustFFT switches to the faster AVX-accelerated algorithms by itself.
For hardware without AVX, the SSE4.1 path is used instead. If a machine has both, the planner picks the fastest available instruction set. The AArch64 story is the same shape: NEON acceleration for 64-bit Arm, again with no configuration on your side. The planner is the dispatch point, so the decision is made once, when the plan is created, and it depends on the CPU features of the machine doing the planning.
That last point carries a practical consequence. A plan is chosen for the hardware that created it. If you build one plan on a workstation and move it to a machine with a different instruction set, you have a plan that was not selected for that CPU. The README does not discuss plan portability across machines, so treat the planner as something you call on the target device rather than a serialisable artifact.
If no accelerated instruction set is available, the scalar code is the fallback, and the feature comments in the package manifest state that plainly: if neither AVX nor SSE4.1 is available, it falls back to the scalar code.
The WASM SIMD path is opt-in and fails loudly
WebAssembly is the exception to the automatic dispatch story, and the documentation calls it out with a bold note. RustFFT supports the fixed-width SIMD extension for WebAssembly, but WebAssembly does not allow dynamic feature detection. Because of that, RustFFT cannot detect CPU features and switch automatically, and instead it unconditionally uses the SIMD code path when the `wasm_simd` crate feature is enabled.
The feature is disabled by default, unlike the others. Enabling it commits you to the target: to execute binaries compiled with `wasm_simd` you need a target browser or runtime which supports fixed-width SIMD, and if you run SIMD accelerated code on an unsupported platform, WebAssembly specifies a trap, leading to immediate execution cancelation.
That failure mode is sharp. It is not a fallback to slower code, it is a trap at the moment the SIMD path is reached. The safe configuration for a browser deployment that must run on unknown engines is to leave `wasm_simd` off and accept the scalar path, or to gate the build on a known engine floor and test there. The README gives no guidance on which browsers or runtimes to target, so that decision sits with you.
For comparison, the native paths degrade: without AVX or SSE4.1 the library simply runs scalar code.
Feature flags trade compile time and binary size against acceleration
The package manifest makes the knobs explicit. The default feature set is:
default = ["avx", "sse", "neon"]and the individual flags are all empty marker features:
avx = []
sse = []
neon = []
fcma = []
wasm_simd = []The `avx` and `sse` flags enable compilation of the accelerated code on x86_64, and disabling them reduces compile time and binary size. The `neon` flag does the same job for AArch64. On any platform other than the one a flag targets, the flag does nothing and the library behaves as if it were not set, which means a single `Cargo.toml` can carry the default set and still build correctly for an embedded or WebAssembly target.
The `fcma` flag is the odd one out. On AArch64 it enables instructions using the FCMA (floating point complex multiply accumulate) instructions from ARMv8.3-A, and it is used instead of the plain NEON code when the CPU supports it. Because the intrinsics are still unstable, this requires a nightly compiler. It is therefore not part of the default set, and adopting it means accepting nightly builds for a specific ARM deployment.
This is the main build-time decision a user makes: keep the defaults and get acceleration on x86_64 and AArch64, or trim the flags to shrink the binary and cut compile time on targets you do not care about.
A three year no-breaking-change promise, with one exception
RustFFT publishes a stability policy, and it is unusually concrete. The stated commitment is to make no more breaking changes for three years. The README text still refers to the latest version as 6.2, which is out of date relative to the 6.4.1 release, but the intent has not been retracted. Version 5.0 arrived at the beginning of 2022 and carried several breaking API changes, and for anyone on a very old version there is an Upgrade Guide at the repository root, `UpgradeGuide4to5.md`, that walks through what the upgrade requires.
There is exactly one exception to the promise, and it is worth reading before you pin a version. RustFFT re-exports pre-1.0 versions of the `num-complex` and `num-traits` crates. In the interest of avoiding ecosystem fragmentation, the maintainers keep up with those crates even when doing so requires major version bumps of their own, and when `num-complex` or `num-traits` release a new major version, RustFFT upgrades as soon as possible. In those releases, the version increase of the upstream crate is described as the only breaking change.
So the practical cost of an upgrade is usually a dependency bump in the manifest rather than a rewrite of transform code, but it is not always zero, and you should read the changelog at `CHANGELOG.md` rather than assume.
The supported Rust version is rustc 1.86 or newer, set as `rust-version` in the package manifest. Minor releases may raise the MSRV, but only to a rustc that has been out for at least six months, which gives downstream users a predictable window to update their toolchain.
Dual MIT and Apache-2.0 licensing with a dual-licensing clause for contributors
RustFFT is licensed under either of the Apache License, Version 2.0, in the file `LICENSE-APACHE`, or the MIT license, in `LICENSE-MIT`, at your option. The package manifest records the same choice as `MIT OR Apache-2.0`, and both license files are present at the repository root.
Dual licensing here is a convenience for downstream users rather than a legal puzzle. A team that already ships Apache-2.0 code can align, and a team that prefers the shorter MIT text can take that instead. The practical point is that you are not obliged to comply with two licenses at once; you pick one. If your organisation has a policy that requires a specific licence for all dependencies, check the manifest field rather than assuming, because tooling that reads only one of the two license files will get a different answer.
Contributions are handled the same way. The project states that unless you explicitly state otherwise, any contribution intentionally submitted for inclusion is dual licensed as above, without additional terms or conditions. That is a standard clause, and it means a contribution is not a route to a different licence for the whole project.
Before sending a pull request, the project asks that you run `cargo fmt`:
cargo fmtThe repository also carries a `rustfmt.toml` at the root and a `tools/` directory, so formatting is configured rather than left to defaults.
Editorial conclusion
RustFFT is the right fit for a Rust codebase that needs FFTs of unusual sizes, in particular prime or mixed-radix lengths where a power-of-two-only library would pad the input and change the result. It is the wrong choice if you need a hard guarantee about the instruction set used, or if you are on WebAssembly and cannot guarantee a runtime with fixed-width SIMD, since the wasm_simd path is unconditional and traps on an unsupported runtime. Before adopting, check the default feature set in Cargo.toml against the targets you ship to, confirm the crate version you resolve still satisfies your rustc version against the 1.86 minimum, and read UpgradeGuide4to5.md if you are moving from a pre-5.0 line. The last push was on 2026-10-17 and the published release is 6.4.1.
Frequently asked questions
How do I add RustFFT to a Rust project?
The crate is published on crates.io as rustfft, and the package manifest pins version 6.4.1 with a rust-version of 1.86. API documentation is published on docs.rs.
Does RustFFT work with transform sizes that are not powers of two?
Yes. The project states that it can compute FFTs of any size, including prime-number sizes, in O(nlogn) time, and its usage example plans a forward transform of size 1234.
Do I need to enable AVX or NEON manually?
No. The documentation says that planning a transform with the FftPlanner on a machine with the avx and fma CPU features automatically switches to AVX-accelerated algorithms, and that NEON works the same way on AArch64. The wasm_simd feature is the exception, since WebAssembly has no dynamic feature detection.
What happens if I enable the wasm_simd feature on an unsupported runtime?
The SIMD path is used unconditionally, and if the code runs on a platform without fixed-width SIMD support, WebAssembly specifies a trap that leads to immediate execution cancelation.
What Rust version does RustFFT require?
It requires rustc 1.86 or newer. The manifest sets rust-version to 1.86, minor releases may raise the MSRV, and the policy requires that any new MSRV has been released at least six months earlier.
What license is RustFFT released under?
It is dual licensed, at your option, under the Apache License 2.0 in LICENSE-APACHE or the MIT license in LICENSE-MIT, and the package manifest records the same choice as MIT OR Apache-2.0.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ejmahler-rustfft)Star history chart for your README
[](https://hysenlabs.com/projects/ejmahler-rustfft)Was this useful?