Skip to content

Commit 250422b

Browse files
committed
Vectorize pre/post R2C processing; overhaul criterion benchmarks
1 parent f9da44c commit 250422b

20 files changed

Lines changed: 1871 additions & 1517 deletions

‎Cargo.lock‎

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ criterion = "0.8.2"
3030
fearless_simd = "0.4.0"
3131
fftw = "0.8.0"
3232
rand = "0.10.1"
33+
realfft = "3.5.0"
3334
utilities = { path = "utilities" }
3435

3536
[[bench]]
@@ -50,7 +51,7 @@ harness = false
5051
required-features = ["complex-nums"]
5152

5253
[[bench]]
53-
name = "heuristic_vs_tune"
54+
name = "planner_mode"
5455
harness = false
5556

5657
[[bench]]
@@ -70,6 +71,10 @@ harness = false
7071
name = "fftw_conserve"
7172
harness = false
7273

74+
[[bench]]
75+
name = "realfft"
76+
harness = false
77+
7378
[profile.profiling]
7479
inherits = "release"
7580
debug = true

‎benches/README.md‎

Lines changed: 183 additions & 60 deletions
Large diffs are not rendered by default.

‎benches/bench.rs‎

Lines changed: 63 additions & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -1,210 +1,74 @@
1-
use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput};
2-
use num_traits::Float;
1+
use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
32
use phastft::options::Options;
43
use phastft::planner::{Direction, PlannerDit32, PlannerDit64};
54
use phastft::{fft_32_dit_with_planner_and_opts, fft_64_dit_with_planner_and_opts};
6-
use rand::distr::StandardUniform;
7-
use rand::prelude::Distribution;
8-
use rand::rngs::SmallRng;
9-
use rand::RngExt;
5+
6+
mod common;
7+
use common::{groups, ids, split_complex, sweep_complex, LENGTHS};
108

119
// IMPORTANT NOTE:
12-
// This benchmark only measures small sizes, which are not the focus of PhastFT.
10+
// This benchmark only measures small-to-mid sizes, which are not the focus of
11+
// PhastFT. Criterion is not a good fit for measuring long-running tasks — see
12+
// examples/benchmark.rs for the harness for large sizes.
1313
//
14-
// Criterion is not a good fit for measuring long-running tasks.
15-
// See examples/benchmark.rs for the benchmarking harness for large sizes.
16-
17-
const LENGTHS: &[usize] = &[
18-
6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
19-
];
20-
21-
fn generate_numbers<T: Float>(n: usize) -> (Vec<T>, Vec<T>)
22-
where
23-
StandardUniform: Distribution<T>,
24-
{
25-
let mut rng = rand::make_rng::<SmallRng>();
26-
27-
let samples: Vec<T> = (&mut rng)
28-
.sample_iter(StandardUniform)
29-
.take(2 * n)
30-
.collect();
31-
32-
let mut reals = vec![T::zero(); n];
33-
let mut imags = vec![T::zero(); n];
34-
35-
for ((z_re, z_im), rand_chunk) in reals
36-
.iter_mut()
37-
.zip(imags.iter_mut())
38-
.zip(samples.chunks_exact(2))
39-
{
40-
*z_re = rand_chunk[0];
41-
*z_im = rand_chunk[1];
42-
}
43-
44-
(reals, imags)
45-
}
46-
47-
fn benchmark_forward_f32(c: &mut Criterion) {
48-
let mut group = c.benchmark_group("Forward f32");
49-
group.plot_config(
50-
criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Logarithmic),
51-
);
52-
group.sample_size(20);
53-
54-
for n in LENGTHS.iter() {
55-
let len = 1 << n; // 2.pow(n)
56-
group.throughput(Throughput::ElementsAndBytes {
57-
elements: len as u64,
58-
bytes: (2 * len * size_of::<f32>()) as u64,
59-
});
60-
61-
let id = "PhastFT DIT";
62-
let options = Options::guess_options(len);
63-
let planner_dit = PlannerDit32::new(len);
64-
65-
group.bench_function(BenchmarkId::new(id, len), |b| {
66-
b.iter_batched(
67-
|| generate_numbers::<f32>(len),
68-
|(mut reals, mut imags)| {
69-
fft_32_dit_with_planner_and_opts(
70-
&mut reals,
71-
&mut imags,
72-
Direction::Forward,
73-
&planner_dit,
74-
&options,
75-
);
76-
std::hint::black_box(&mut reals);
77-
std::hint::black_box(&mut imags);
78-
},
79-
BatchSize::SmallInput,
80-
);
81-
});
82-
}
83-
group.finish();
84-
}
85-
86-
fn benchmark_inverse_f32(c: &mut Criterion) {
87-
let mut group = c.benchmark_group("Inverse f32");
88-
group.plot_config(
89-
criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Logarithmic),
90-
);
91-
group.sample_size(20);
92-
93-
for n in LENGTHS.iter() {
94-
let len = 1 << n; // 2.pow(n)
95-
group.throughput(Throughput::ElementsAndBytes {
96-
elements: len as u64,
97-
bytes: (2 * len * size_of::<f32>()) as u64,
98-
});
99-
100-
let id = "PhastFT DIT";
101-
let options = Options::guess_options(len);
102-
let planner_dit = PlannerDit32::new(len);
103-
104-
group.bench_function(BenchmarkId::new(id, len), |b| {
105-
b.iter_batched(
106-
|| generate_numbers::<f32>(len),
107-
|(mut reals, mut imags)| {
108-
fft_32_dit_with_planner_and_opts(
109-
&mut reals,
110-
&mut imags,
111-
Direction::Reverse,
112-
&planner_dit,
113-
&options,
14+
// The PhastFT, RustFFT, and FFTW bench binaries all write into the same
15+
// `target/criterion/<group>/<id>/<size>/` tree; criterion does NOT auto-
16+
// aggregate across binaries, so use `benches/plot_criterion_overlay.py` to
17+
// produce a single overlay plot per group after running them all.
18+
19+
macro_rules! phastft_c2c {
20+
($name:ident, $float:ty, $planner:ty, $fft:ident, $dir:expr, $group:expr) => {
21+
fn $name(c: &mut Criterion) {
22+
sweep_complex::<$float, _>(c, $group, LENGTHS, |g, len| {
23+
let opts = Options::guess_options(len);
24+
let planner = <$planner>::new(len);
25+
g.bench_function(BenchmarkId::new(ids::PHASTFT_DIT, len), |b| {
26+
b.iter_batched(
27+
|| split_complex::<$float>(len),
28+
|(mut reals, mut imags)| {
29+
$fft(&mut reals, &mut imags, $dir, &planner, &opts);
30+
std::hint::black_box((&mut reals, &mut imags));
31+
},
32+
BatchSize::SmallInput,
11433
);
115-
std::hint::black_box(&mut reals);
116-
std::hint::black_box(&mut imags);
117-
},
118-
BatchSize::SmallInput,
119-
);
120-
});
121-
}
122-
group.finish();
34+
});
35+
});
36+
}
37+
};
12338
}
12439

125-
fn benchmark_forward_f64(c: &mut Criterion) {
126-
let mut group = c.benchmark_group("Forward f64");
127-
group.plot_config(
128-
criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Logarithmic),
129-
);
130-
group.sample_size(20);
131-
132-
for n in LENGTHS.iter() {
133-
let len = 1 << n; // 2.pow(n)
134-
group.throughput(Throughput::ElementsAndBytes {
135-
elements: len as u64,
136-
bytes: (2 * len * size_of::<f64>()) as u64,
137-
});
138-
139-
let id = "PhastFT DIT";
140-
let options = Options::guess_options(len);
141-
let planner_dit = PlannerDit64::new(len);
142-
143-
group.bench_function(BenchmarkId::new(id, len), |b| {
144-
b.iter_batched(
145-
|| generate_numbers::<f64>(len),
146-
|(mut reals, mut imags)| {
147-
fft_64_dit_with_planner_and_opts(
148-
&mut reals,
149-
&mut imags,
150-
Direction::Forward,
151-
&planner_dit,
152-
&options,
153-
);
154-
std::hint::black_box(&mut reals);
155-
std::hint::black_box(&mut imags);
156-
},
157-
BatchSize::SmallInput,
158-
);
159-
});
160-
}
161-
group.finish();
162-
}
163-
164-
fn benchmark_inverse_f64(c: &mut Criterion) {
165-
let mut group = c.benchmark_group("Inverse f64");
166-
group.plot_config(
167-
criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Logarithmic),
168-
);
169-
group.sample_size(20);
170-
171-
for n in LENGTHS.iter() {
172-
let len = 1 << n; // 2.pow(n)
173-
group.throughput(Throughput::ElementsAndBytes {
174-
elements: len as u64,
175-
bytes: (2 * len * size_of::<f64>()) as u64,
176-
});
177-
178-
let id = "PhastFT DIT";
179-
let options = Options::guess_options(len);
180-
let planner_dit = PlannerDit64::new(len);
181-
182-
group.bench_function(BenchmarkId::new(id, len), |b| {
183-
b.iter_batched(
184-
|| generate_numbers::<f64>(len),
185-
|(mut reals, mut imags)| {
186-
fft_64_dit_with_planner_and_opts(
187-
&mut reals,
188-
&mut imags,
189-
Direction::Reverse,
190-
&planner_dit,
191-
&options,
192-
);
193-
std::hint::black_box(&mut reals);
194-
std::hint::black_box(&mut imags);
195-
},
196-
BatchSize::SmallInput,
197-
);
198-
});
199-
}
200-
group.finish();
201-
}
202-
203-
criterion_group!(
204-
benches,
205-
benchmark_forward_f32,
206-
benchmark_inverse_f32,
207-
benchmark_forward_f64,
208-
benchmark_inverse_f64,
40+
phastft_c2c!(
41+
fwd_f32,
42+
f32,
43+
PlannerDit32,
44+
fft_32_dit_with_planner_and_opts,
45+
Direction::Forward,
46+
groups::C2C_FORWARD_F32
20947
);
48+
phastft_c2c!(
49+
inv_f32,
50+
f32,
51+
PlannerDit32,
52+
fft_32_dit_with_planner_and_opts,
53+
Direction::Reverse,
54+
groups::C2C_INVERSE_F32
55+
);
56+
phastft_c2c!(
57+
fwd_f64,
58+
f64,
59+
PlannerDit64,
60+
fft_64_dit_with_planner_and_opts,
61+
Direction::Forward,
62+
groups::C2C_FORWARD_F64
63+
);
64+
phastft_c2c!(
65+
inv_f64,
66+
f64,
67+
PlannerDit64,
68+
fft_64_dit_with_planner_and_opts,
69+
Direction::Reverse,
70+
groups::C2C_INVERSE_F64
71+
);
72+
73+
criterion_group!(benches, fwd_f32, inv_f32, fwd_f64, inv_f64);
21074
criterion_main!(benches);

0 commit comments

Comments
 (0)