Skip to content
Merged
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## [6.4]
Released 12th June 2025
### Added
- Implemented a new code path for out-of-place FFTs where the input vector is immutable: `Fft::process_immutable_with_scratch` (Thanks to @michaelciraci) (#157)
### Changed
- Refactored some RustFFT internals. Gives a small reduction in binary size, among other benefits (#161)
- Upgraded to 2021 edition (#162)


## [6.3]
Released 17th April 2025
### Changed
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[package]

name = "rustfft"
version = "6.3.0"
version = "6.4.0"
authors = ["Allen Welkie <allen.welkie at gmail>", "Elliott Mahler <join.together at gmail>"]
edition = "2018"
edition = "2021"
rust-version = "1.61"

description = "High-performance FFT library written in pure Rust."
Expand Down
20 changes: 10 additions & 10 deletions benches/bench_rustfft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ fn bench_good_thomas(b: &mut Bencher, width: usize, height: usize) {
let width_fft = planner.plan_fft_forward(width);
let height_fft = planner.plan_fft_forward(height);

let fft : Arc<Fft<f32>> = Arc::new(GoodThomasAlgorithm::new(width_fft, height_fft));
let fft : Arc<dyn Fft<f32>> = Arc::new(GoodThomasAlgorithm::new(width_fft, height_fft));

let mut buffer = vec![Complex::zero(); width * height];
let mut scratch = vec![Complex::zero(); fft.get_inplace_scratch_len()];
Expand All @@ -228,7 +228,7 @@ fn bench_good_thomas_setup(b: &mut Bencher, width: usize, height: usize) {
let height_fft = planner.plan_fft_forward(height);

b.iter(|| {
let fft : Arc<Fft<f32>> = Arc::new(GoodThomasAlgorithm::new(Arc::clone(&width_fft), Arc::clone(&height_fft)));
let fft : Arc<dyn Fft<f32>> = Arc::new(GoodThomasAlgorithm::new(Arc::clone(&width_fft), Arc::clone(&height_fft)));
test::black_box(fft);
});
}
Expand All @@ -251,7 +251,7 @@ fn bench_mixed_radix_setup(b: &mut Bencher, width: usize, height: usize) {
let height_fft = planner.plan_fft_forward(height);

b.iter(|| {
let fft : Arc<Fft<f32>> = Arc::new(MixedRadix::new(Arc::clone(&width_fft), Arc::clone(&height_fft)));
let fft : Arc<dyn Fft<f32>> = Arc::new(MixedRadix::new(Arc::clone(&width_fft), Arc::clone(&height_fft)));
test::black_box(fft);
});
}
Expand All @@ -274,7 +274,7 @@ fn bench_small_mixed_radix_setup(b: &mut Bencher, width: usize, height: usize) {
let height_fft = planner.plan_fft_forward(height);

b.iter(|| {
let fft : Arc<Fft<f32>> = Arc::new(MixedRadixSmall::new(Arc::clone(&width_fft), Arc::clone(&height_fft)));
let fft : Arc<dyn Fft<f32>> = Arc::new(MixedRadixSmall::new(Arc::clone(&width_fft), Arc::clone(&height_fft)));
test::black_box(fft);
});
}
Expand All @@ -294,7 +294,7 @@ fn bench_mixed_radix(b: &mut Bencher, width: usize, height: usize) {
let width_fft = planner.plan_fft_forward(width);
let height_fft = planner.plan_fft_forward(height);

let fft : Arc<Fft<_>> = Arc::new(MixedRadix::new(width_fft, height_fft));
let fft : Arc<dyn Fft<_>> = Arc::new(MixedRadix::new(width_fft, height_fft));

let mut buffer = vec![Complex{re: 0_f32, im: 0_f32}; fft.len()];
let mut scratch = vec![Complex{re: 0_f32, im: 0_f32}; fft.get_inplace_scratch_len()];
Expand All @@ -310,7 +310,7 @@ fn bench_mixed_radix(b: &mut Bencher, width: usize, height: usize) {
#[bench] fn mixed_radix_2048_3(b: &mut Bencher) { bench_mixed_radix(b, 2048, 3); }
#[bench] fn mixed_radix_2048_2187(b: &mut Bencher) { bench_mixed_radix(b, 2048, 2187); }

fn plan_butterfly_fft(len: usize) -> Arc<Fft<f32>> {
fn plan_butterfly_fft(len: usize) -> Arc<dyn Fft<f32>> {
match len {
2 => Arc::new(Butterfly2::new(FftDirection::Forward)),
3 => Arc::new(Butterfly3::new(FftDirection::Forward)),
Expand All @@ -332,7 +332,7 @@ fn bench_mixed_radix_small(b: &mut Bencher, width: usize, height: usize) {
let width_fft = plan_butterfly_fft(width);
let height_fft = plan_butterfly_fft(height);

let fft : Arc<Fft<_>> = Arc::new(MixedRadixSmall::new(width_fft, height_fft));
let fft : Arc<dyn Fft<_>> = Arc::new(MixedRadixSmall::new(width_fft, height_fft));

let mut signal = vec![Complex{re: 0_f32, im: 0_f32}; width * height];
let mut spectrum = signal.clone();
Expand All @@ -351,7 +351,7 @@ fn bench_good_thomas_small(b: &mut Bencher, width: usize, height: usize) {
let width_fft = plan_butterfly_fft(width);
let height_fft = plan_butterfly_fft(height);

let fft : Arc<Fft<_>> = Arc::new(GoodThomasAlgorithmSmall::new(width_fft, height_fft));
let fft : Arc<dyn Fft<_>> = Arc::new(GoodThomasAlgorithmSmall::new(width_fft, height_fft));

let mut signal = vec![Complex{re: 0_f32, im: 0_f32}; width * height];
let mut spectrum = signal.clone();
Expand All @@ -371,7 +371,7 @@ fn bench_raders_scalar(b: &mut Bencher, len: usize) {
let mut planner = rustfft::FftPlanner::new();
let inner_fft = planner.plan_fft_forward(len - 1);

let fft : Arc<Fft<_>> = Arc::new(RadersAlgorithm::new(inner_fft));
let fft : Arc<dyn Fft<_>> = Arc::new(RadersAlgorithm::new(inner_fft));

let mut buffer = vec![Complex{re: 0_f32, im: 0_f32}; len];
let mut scratch = vec![Complex{re: 0_f32, im: 0_f32}; fft.get_inplace_scratch_len()];
Expand Down Expand Up @@ -399,7 +399,7 @@ fn bench_raders_scalar(b: &mut Bencher, len: usize) {
fn bench_bluesteins_scalar_prime(b: &mut Bencher, len: usize) {
let mut planner = rustfft::FftPlanner::new();
let inner_fft = planner.plan_fft_forward((len * 2 - 1).checked_next_power_of_two().unwrap());
let fft : Arc<Fft<f32>> = Arc::new(BluesteinsAlgorithm::new(len, inner_fft));
let fft : Arc<dyn Fft<f32>> = Arc::new(BluesteinsAlgorithm::new(len, inner_fft));

let mut buffer = vec![Zero::zero(); len];
let mut scratch = vec![Zero::zero(); fft.get_inplace_scratch_len()];
Expand Down
16 changes: 8 additions & 8 deletions benches/bench_rustfft_scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ fn bench_good_thomas(b: &mut Bencher, width: usize, height: usize) {
let width_fft = planner.plan_fft_forward(width);
let height_fft = planner.plan_fft_forward(height);

let fft: Arc<Fft<f32>> = Arc::new(GoodThomasAlgorithm::new(width_fft, height_fft));
let fft: Arc<dyn Fft<f32>> = Arc::new(GoodThomasAlgorithm::new(width_fft, height_fft));

let mut buffer = vec![Complex::zero(); width * height];
let mut scratch = vec![Complex::zero(); fft.get_inplace_scratch_len()];
Expand Down Expand Up @@ -320,7 +320,7 @@ fn bench_good_thomas_setup(b: &mut Bencher, width: usize, height: usize) {
let height_fft = planner.plan_fft_forward(height);

b.iter(|| {
let fft: Arc<Fft<f32>> = Arc::new(GoodThomasAlgorithm::new(
let fft: Arc<dyn Fft<f32>> = Arc::new(GoodThomasAlgorithm::new(
Arc::clone(&width_fft),
Arc::clone(&height_fft),
));
Expand Down Expand Up @@ -368,7 +368,7 @@ fn bench_mixed_radix(b: &mut Bencher, width: usize, height: usize) {
let width_fft = planner.plan_fft_forward(width);
let height_fft = planner.plan_fft_forward(height);

let fft: Arc<Fft<_>> = Arc::new(MixedRadix::new(width_fft, height_fft));
let fft: Arc<dyn Fft<_>> = Arc::new(MixedRadix::new(width_fft, height_fft));

let mut buffer = vec![
Complex {
Expand Down Expand Up @@ -413,7 +413,7 @@ fn mixed_radix_0032_27(b: &mut Bencher) {
//#[bench] fn mixed_radix_2048_3(b: &mut Bencher) { bench_mixed_radix(b, 2048, 3); }
//#[bench] fn mixed_radix_2048_2187(b: &mut Bencher) { bench_mixed_radix(b, 2048, 2187); }

fn plan_butterfly_fft(len: usize) -> Arc<Fft<f32>> {
fn plan_butterfly_fft(len: usize) -> Arc<dyn Fft<f32>> {
match len {
2 => Arc::new(Butterfly2::new(FftDirection::Forward)),
3 => Arc::new(Butterfly3::new(FftDirection::Forward)),
Expand All @@ -434,7 +434,7 @@ fn bench_mixed_radix_small(b: &mut Bencher, width: usize, height: usize) {
let width_fft = plan_butterfly_fft(width);
let height_fft = plan_butterfly_fft(height);

let fft: Arc<Fft<_>> = Arc::new(MixedRadixSmall::new(width_fft, height_fft));
let fft: Arc<dyn Fft<_>> = Arc::new(MixedRadixSmall::new(width_fft, height_fft));

let mut signal = vec![
Complex {
Expand Down Expand Up @@ -472,7 +472,7 @@ fn bench_good_thomas_small(b: &mut Bencher, width: usize, height: usize) {
let width_fft = plan_butterfly_fft(width);
let height_fft = plan_butterfly_fft(height);

let fft: Arc<Fft<_>> = Arc::new(GoodThomasAlgorithmSmall::new(width_fft, height_fft));
let fft: Arc<dyn Fft<_>> = Arc::new(GoodThomasAlgorithmSmall::new(width_fft, height_fft));

let mut signal = vec![
Complex {
Expand Down Expand Up @@ -511,7 +511,7 @@ fn bench_raders_scalar(b: &mut Bencher, len: usize) {
let mut planner = rustfft::FftPlannerScalar::new();
let inner_fft = planner.plan_fft_forward(len - 1);

let fft: Arc<Fft<_>> = Arc::new(RadersAlgorithm::new(inner_fft));
let fft: Arc<dyn Fft<_>> = Arc::new(RadersAlgorithm::new(inner_fft));

let mut buffer = vec![
Complex {
Expand Down Expand Up @@ -553,7 +553,7 @@ fn bench_raders_scalar(b: &mut Bencher, len: usize) {
fn bench_bluesteins_scalar_prime(b: &mut Bencher, len: usize) {
let mut planner = rustfft::FftPlannerScalar::new();
let inner_fft = planner.plan_fft_forward((len * 2 - 1).checked_next_power_of_two().unwrap());
let fft: Arc<Fft<f32>> = Arc::new(BluesteinsAlgorithm::new(len, inner_fft));
let fft: Arc<dyn Fft<f32>> = Arc::new(BluesteinsAlgorithm::new(len, inner_fft));

let mut buffer = vec![Zero::zero(); len];
let mut scratch = vec![Zero::zero(); fft.get_inplace_scratch_len()];
Expand Down
8 changes: 4 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//!
//! ### Usage
//!
//! The recommended way to use RustFFT is to create a [`FftPlanner`](crate::FftPlanner) instance and then call its
//! The recommended way to use RustFFT is to create a [`FftPlanner`] instance and then call its
//! [`plan_fft`](crate::FftPlanner::plan_fft) method. This method will automatically choose which FFT algorithms are best
//! for a given size and initialize the required buffers and precomputed data.
//!
Expand All @@ -26,7 +26,7 @@
//! let mut buffer = vec![Complex{ re: 0.0f32, im: 0.0f32 }; 1234];
//! fft.process(&mut buffer);
//! ```
//! The planner returns trait objects of the [`Fft`](crate::Fft) trait, allowing for FFT sizes that aren't known
//! The planner returns trait objects of the [`Fft`] trait, allowing for FFT sizes that aren't known
//! until runtime.
//!
//! RustFFT also exposes individual FFT algorithms. For example, if you know beforehand that you need a power-of-two FFT, you can
Expand All @@ -42,9 +42,9 @@
//! fft.process(&mut buffer);
//! ```
//!
//! For the vast majority of situations, simply using the [`FftPlanner`](crate::FftPlanner) will be enough, but
//! For the vast majority of situations, simply using the [`FftPlanner`] will be enough, but
//! advanced users may have better insight than the planner into which algorithms are best for a specific size. See the
//! [`algorithm`](crate::algorithm) module for a complete list of scalar algorithms implemented by RustFFT.
//! [`algorithm`] module for a complete list of scalar algorithms implemented by RustFFT.
//!
//! Users should beware, however, that bypassing the planner will disable all AVX, SSE, Neon, and WASM SIMD optimizations.
//!
Expand Down