|
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}; |
3 | 2 | use phastft::options::Options; |
4 | 3 | use phastft::planner::{Direction, PlannerDit32, PlannerDit64}; |
5 | 4 | 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}; |
10 | 8 |
|
11 | 9 | // 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. |
13 | 13 | // |
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, |
114 | 33 | ); |
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 | + }; |
123 | 38 | } |
124 | 39 |
|
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 |
209 | 47 | ); |
| 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); |
210 | 74 | criterion_main!(benches); |
0 commit comments