Skip to main content

p3_field_testing/
bench_func.rs

1use alloc::format;
2use alloc::vec::Vec;
3use core::hint::black_box;
4use core::ops::Div;
5
6use criterion::{BatchSize, Criterion};
7use p3_field::{Algebra, Field, PrimeCharacteristicRing, chunked_linear_combination};
8use rand::distr::StandardUniform;
9use rand::prelude::Distribution;
10use rand::rngs::SmallRng;
11use rand::{RngExt, SeedableRng};
12
13/// Not useful for benchmarking prime fields as multiplication is too fast but
14/// handy for extension fields.
15pub fn benchmark_mul<F: Field>(c: &mut Criterion, name: &str)
16where
17    StandardUniform: Distribution<F>,
18{
19    let mut rng = SmallRng::seed_from_u64(1);
20    let x = rng.random::<F>();
21    let y = rng.random::<F>();
22    c.bench_function(&format!("{name} mul"), |b| {
23        b.iter(|| black_box(black_box(x) * black_box(y)));
24    });
25}
26
27pub fn benchmark_square<F: Field>(c: &mut Criterion, name: &str)
28where
29    StandardUniform: Distribution<F>,
30{
31    let mut rng = SmallRng::seed_from_u64(1);
32    let x = rng.random::<F>();
33    c.bench_function(&format!("{name} square"), |b| {
34        b.iter(|| black_box(black_box(x).square()));
35    });
36}
37
38pub fn benchmark_inv<F: Field>(c: &mut Criterion, name: &str)
39where
40    StandardUniform: Distribution<F>,
41{
42    let mut rng = SmallRng::seed_from_u64(1);
43    let x = rng.random::<F>();
44    c.bench_function(&format!("{name} inv"), |b| {
45        b.iter(|| black_box(black_box(x)).inverse());
46    });
47}
48
49pub fn benchmark_mul_2exp<R: PrimeCharacteristicRing + Copy, const REPS: usize>(
50    c: &mut Criterion,
51    name: &str,
52    val: u64,
53) where
54    StandardUniform: Distribution<R>,
55{
56    let mut rng = SmallRng::seed_from_u64(1);
57    let mut input = Vec::new();
58    for _ in 0..REPS {
59        input.push(rng.random::<R>());
60    }
61    c.bench_function(&format!("{name} mul_2exp_u64 {val}"), |b| {
62        b.iter(|| input.iter_mut().for_each(|i| *i = i.mul_2exp_u64(val)));
63    });
64}
65
66pub fn benchmark_halve<F: Field, const REPS: usize>(c: &mut Criterion, name: &str)
67where
68    StandardUniform: Distribution<F>,
69{
70    let mut rng = SmallRng::seed_from_u64(1);
71    let mut input = Vec::new();
72    for _ in 0..REPS {
73        input.push(rng.random::<F>());
74    }
75    c.bench_function(&format!("{name} halve. Num Reps: {REPS}"), |b| {
76        b.iter(|| input.iter_mut().for_each(|i| *i = i.halve()));
77    });
78}
79
80pub fn benchmark_div_2exp<F: Field, const REPS: usize>(c: &mut Criterion, name: &str, val: u64)
81where
82    StandardUniform: Distribution<F>,
83{
84    let mut rng = SmallRng::seed_from_u64(1);
85    let mut input = Vec::new();
86    for _ in 0..REPS {
87        input.push(rng.random::<F>());
88    }
89    c.bench_function(&format!("{name} div_2exp_u64 {val}"), |b| {
90        b.iter(|| input.iter_mut().for_each(|i| *i = i.div_2exp_u64(val)));
91    });
92}
93
94/// Benchmark the time taken to sum an array [[F; N]; REPS] by summing each array
95/// [F; N] using .sum() method and accumulating the sums into an accumulator.
96///
97/// Making N larger and REPS smaller (vs the opposite) leans the benchmark more sensitive towards
98/// the latency (resp throughput) of the sum method.
99pub fn benchmark_iter_sum<R: PrimeCharacteristicRing + Copy, const N: usize, const REPS: usize>(
100    c: &mut Criterion,
101    name: &str,
102) where
103    StandardUniform: Distribution<R>,
104{
105    let mut rng = SmallRng::seed_from_u64(1);
106    let mut input = Vec::new();
107    for _ in 0..REPS {
108        input.push(rng.random::<[R; N]>());
109    }
110    c.bench_function(&format!("{name} sum/{REPS}, {N}"), |b| {
111        b.iter(|| {
112            let mut acc = R::ZERO;
113            for row in &mut input {
114                acc += row.iter().copied().sum();
115            }
116            acc
117        });
118    });
119}
120
121/// Benchmark the time taken to sum an array [[F; N]; REPS] by summing each array
122/// [F; N] using sum_array method and accumulating the sums into an accumulator.
123///
124/// Making N larger and REPS smaller (vs the opposite) leans the benchmark more sensitive towards
125/// the latency (resp throughput) of the sum method.
126pub fn benchmark_sum_array<R: PrimeCharacteristicRing + Copy, const N: usize, const REPS: usize>(
127    c: &mut Criterion,
128    name: &str,
129) where
130    StandardUniform: Distribution<R>,
131{
132    let mut rng = SmallRng::seed_from_u64(1);
133    let mut input = Vec::new();
134    for _ in 0..REPS {
135        input.push(rng.random::<[R; N]>());
136    }
137    c.bench_function(&format!("{name} tree sum/{REPS}, {N}"), |b| {
138        b.iter(|| {
139            let mut acc = R::ZERO;
140            for row in &mut input {
141                acc += R::sum_array::<N>(row);
142            }
143            acc
144        });
145    });
146}
147
148/// Benchmark the time taken to do dot products on a pair of `[R; N]` arrays.
149///
150/// These numbers get more trustworthy as N increases. Small N leads to the
151/// computation being too fast to be measured accurately.
152pub fn benchmark_dot_array<R: PrimeCharacteristicRing + Copy, const N: usize>(
153    c: &mut Criterion,
154    name: &str,
155) where
156    StandardUniform: Distribution<R>,
157{
158    let mut rng = SmallRng::seed_from_u64(1);
159    let lhs = rng.random::<[R; N]>();
160    let rhs = rng.random::<[R; N]>();
161
162    c.bench_function(&format!("{name} dot product/{N}"), |b| {
163        b.iter(|| black_box(R::dot_product(black_box(&lhs), black_box(&rhs))));
164    });
165}
166
167/// Benchmark the time taken to do mixed dot products on a pair of `[A; N]` and `[F; N]` arrays.
168pub fn benchmark_mixed_dot_array<A: Algebra<F> + Copy, F: Field, const N: usize>(
169    c: &mut Criterion,
170    name: &str,
171) where
172    StandardUniform: Distribution<A> + Distribution<F>,
173{
174    let mut rng = SmallRng::seed_from_u64(1);
175    let a = rng.random::<[A; N]>();
176    let f = rng.random::<[F; N]>();
177    c.bench_function(&format!("{name} mixed dot product/{N}"), |b| {
178        b.iter(|| black_box(A::mixed_dot_product(black_box(&a), black_box(&f))));
179    });
180}
181
182/// Benchmark the time taken to add two slices together.
183pub fn benchmark_add_slices<F: Field, const LENGTH: usize>(c: &mut Criterion, name: &str)
184where
185    StandardUniform: Distribution<F>,
186{
187    let mut rng = SmallRng::seed_from_u64(1);
188    let mut slice_1 = Vec::new();
189    let mut slice_2 = Vec::new();
190    for _ in 0..LENGTH {
191        slice_1.push(rng.random());
192        slice_2.push(rng.random());
193    }
194    c.bench_function(&format!("{name} add slices/{LENGTH}"), |b| {
195        let mut in_slice = slice_1.clone();
196        b.iter(|| {
197            F::add_slices(&mut in_slice, &slice_2);
198        });
199    });
200}
201
202pub fn benchmark_add_latency<R: PrimeCharacteristicRing + Copy, const N: usize>(
203    c: &mut Criterion,
204    name: &str,
205) where
206    StandardUniform: Distribution<R>,
207{
208    c.bench_function(&format!("add-latency/{N} {name}"), |b| {
209        b.iter_batched(
210            || {
211                let mut rng = SmallRng::seed_from_u64(1);
212                let mut vec = Vec::new();
213                for _ in 0..N {
214                    vec.push(rng.random::<R>());
215                }
216                vec
217            },
218            |x| x.iter().fold(R::ZERO, |x, y| x + *y),
219            BatchSize::SmallInput,
220        );
221    });
222}
223
224pub fn benchmark_add_throughput<R: PrimeCharacteristicRing + Copy, const N: usize>(
225    c: &mut Criterion,
226    name: &str,
227) where
228    StandardUniform: Distribution<R>,
229{
230    c.bench_function(&format!("add-throughput/{N} {name}"), |b| {
231        b.iter_batched(
232            || {
233                let mut rng = SmallRng::seed_from_u64(1);
234                (
235                    rng.random::<R>(),
236                    rng.random::<R>(),
237                    rng.random::<R>(),
238                    rng.random::<R>(),
239                    rng.random::<R>(),
240                    rng.random::<R>(),
241                    rng.random::<R>(),
242                    rng.random::<R>(),
243                    rng.random::<R>(),
244                    rng.random::<R>(),
245                )
246            },
247            |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
248                for _ in 0..N {
249                    (a, b, c, d, e, f, g, h, i, j) = (
250                        a + b,
251                        b + c,
252                        c + d,
253                        d + e,
254                        e + f,
255                        f + g,
256                        g + h,
257                        h + i,
258                        i + j,
259                        j + a,
260                    );
261                }
262                (a, b, c, d, e, f, g, h, i, j)
263            },
264            BatchSize::SmallInput,
265        );
266    });
267}
268
269pub fn benchmark_sub_latency<R: PrimeCharacteristicRing + Copy, const N: usize>(
270    c: &mut Criterion,
271    name: &str,
272) where
273    StandardUniform: Distribution<R>,
274{
275    c.bench_function(&format!("sub-latency/{N} {name}"), |b| {
276        b.iter_batched(
277            || {
278                let mut rng = SmallRng::seed_from_u64(1);
279                let mut vec = Vec::new();
280                for _ in 0..N {
281                    vec.push(rng.random::<R>());
282                }
283                vec
284            },
285            |x| x.iter().fold(R::ZERO, |x, y| x - *y),
286            BatchSize::SmallInput,
287        );
288    });
289}
290
291pub fn benchmark_sub_throughput<R: PrimeCharacteristicRing + Copy, const N: usize>(
292    c: &mut Criterion,
293    name: &str,
294) where
295    StandardUniform: Distribution<R>,
296{
297    c.bench_function(&format!("sub-throughput/{N} {name}"), |b| {
298        b.iter_batched(
299            || {
300                let mut rng = SmallRng::seed_from_u64(1);
301                (
302                    rng.random::<R>(),
303                    rng.random::<R>(),
304                    rng.random::<R>(),
305                    rng.random::<R>(),
306                    rng.random::<R>(),
307                    rng.random::<R>(),
308                    rng.random::<R>(),
309                    rng.random::<R>(),
310                    rng.random::<R>(),
311                    rng.random::<R>(),
312                )
313            },
314            |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
315                for _ in 0..N {
316                    (a, b, c, d, e, f, g, h, i, j) = (
317                        a - b,
318                        b - c,
319                        c - d,
320                        d - e,
321                        e - f,
322                        f - g,
323                        g - h,
324                        h - i,
325                        i - j,
326                        j - a,
327                    );
328                }
329                (a, b, c, d, e, f, g, h, i, j)
330            },
331            BatchSize::SmallInput,
332        );
333    });
334}
335
336pub fn benchmark_mul_latency<R: PrimeCharacteristicRing + Copy, const N: usize>(
337    c: &mut Criterion,
338    name: &str,
339) where
340    StandardUniform: Distribution<R>,
341{
342    c.bench_function(&format!("mul-latency/{N} {name}"), |b| {
343        b.iter_batched(
344            || {
345                let mut rng = SmallRng::seed_from_u64(1);
346                let mut vec = Vec::new();
347                for _ in 0..N {
348                    vec.push(rng.random::<R>());
349                }
350                vec
351            },
352            |x| x.iter().fold(R::ONE, |x, y| x * *y),
353            BatchSize::SmallInput,
354        );
355    });
356}
357
358pub fn benchmark_mul_throughput<R: PrimeCharacteristicRing + Copy, const N: usize>(
359    c: &mut Criterion,
360    name: &str,
361) where
362    StandardUniform: Distribution<R>,
363{
364    c.bench_function(&format!("mul-throughput/{N} {name}"), |b| {
365        b.iter_batched(
366            || {
367                let mut rng = SmallRng::seed_from_u64(1);
368                (
369                    rng.random::<R>(),
370                    rng.random::<R>(),
371                    rng.random::<R>(),
372                    rng.random::<R>(),
373                    rng.random::<R>(),
374                    rng.random::<R>(),
375                    rng.random::<R>(),
376                    rng.random::<R>(),
377                    rng.random::<R>(),
378                    rng.random::<R>(),
379                )
380            },
381            |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
382                for _ in 0..N {
383                    (a, b, c, d, e, f, g, h, i, j) = (
384                        a * b,
385                        b * c,
386                        c * d,
387                        d * e,
388                        e * f,
389                        f * g,
390                        g * h,
391                        h * i,
392                        i * j,
393                        j * a,
394                    );
395                }
396                (a, b, c, d, e, f, g, h, i, j)
397            },
398            BatchSize::SmallInput,
399        );
400    });
401}
402
403pub fn benchmark_div_latency<R: PrimeCharacteristicRing + Copy + Div<Output = R>, const N: usize>(
404    c: &mut Criterion,
405    name: &str,
406) where
407    StandardUniform: Distribution<R>,
408{
409    c.bench_function(&format!("div-latency/{N} {name}"), |b| {
410        b.iter_batched(
411            || {
412                let mut rng = SmallRng::seed_from_u64(1);
413                let init = rng.random::<R>();
414                let mut vec = Vec::with_capacity(N);
415                for _ in 0..N {
416                    vec.push(rng.random::<R>());
417                }
418                (init, vec)
419            },
420            |(init, vec)| vec.iter().fold(init, |x, y| x / *y),
421            BatchSize::SmallInput,
422        );
423    });
424}
425
426pub fn benchmark_div_throughput<
427    R: PrimeCharacteristicRing + Copy + Div<Output = R>,
428    const N: usize,
429>(
430    c: &mut Criterion,
431    name: &str,
432) where
433    StandardUniform: Distribution<R>,
434{
435    c.bench_function(&format!("div-throughput/{N} {name}"), |b| {
436        b.iter_batched(
437            || {
438                let mut rng = SmallRng::seed_from_u64(1);
439                (
440                    rng.random::<R>(),
441                    rng.random::<R>(),
442                    rng.random::<R>(),
443                    rng.random::<R>(),
444                    rng.random::<R>(),
445                    rng.random::<R>(),
446                    rng.random::<R>(),
447                    rng.random::<R>(),
448                    rng.random::<R>(),
449                    rng.random::<R>(),
450                )
451            },
452            |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
453                for _ in 0..N {
454                    (a, b, c, d, e, f, g, h, i, j) = (
455                        a / b,
456                        b / c,
457                        c / d,
458                        d / e,
459                        e / f,
460                        f / g,
461                        g / h,
462                        h / i,
463                        i / j,
464                        j / a,
465                    );
466                }
467                (a, b, c, d, e, f, g, h, i, j)
468            },
469            BatchSize::SmallInput,
470        );
471    });
472}
473
474pub fn benchmark_base_mul_latency<F: Field, A: Algebra<F> + Copy, const N: usize>(
475    c: &mut Criterion,
476    name: &str,
477) where
478    StandardUniform: Distribution<F> + Distribution<A>,
479{
480    c.bench_function(&format!("base_mul-latency/{N} {name}"), |b| {
481        b.iter_batched(
482            || {
483                let mut rng = SmallRng::seed_from_u64(1);
484                let mut vec = Vec::new();
485                for _ in 0..N {
486                    vec.push(rng.random::<F>());
487                }
488                let init_val = rng.random::<A>();
489                (vec, init_val)
490            },
491            |(x, init_val)| x.iter().fold(init_val, |x, y| x * *y),
492            BatchSize::SmallInput,
493        );
494    });
495}
496
497pub fn benchmark_base_mul_throughput<F: Field, A: Algebra<F> + Copy, const N: usize>(
498    c: &mut Criterion,
499    name: &str,
500) where
501    StandardUniform: Distribution<F> + Distribution<A>,
502{
503    c.bench_function(&format!("base_mul-throughput/{N} {name}"), |b| {
504        b.iter_batched(
505            || {
506                let mut rng = SmallRng::seed_from_u64(1);
507                let a_tuple = (
508                    rng.random::<A>(),
509                    rng.random::<A>(),
510                    rng.random::<A>(),
511                    rng.random::<A>(),
512                    rng.random::<A>(),
513                    rng.random::<A>(),
514                    rng.random::<A>(),
515                    rng.random::<A>(),
516                    rng.random::<A>(),
517                    rng.random::<A>(),
518                );
519                let f_tuple = (
520                    rng.random::<F>(),
521                    rng.random::<F>(),
522                    rng.random::<F>(),
523                    rng.random::<F>(),
524                    rng.random::<F>(),
525                    rng.random::<F>(),
526                    rng.random::<F>(),
527                    rng.random::<F>(),
528                    rng.random::<F>(),
529                    rng.random::<F>(),
530                );
531                (a_tuple, f_tuple)
532            },
533            |(
534                (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j),
535                (a_f, b_f, c_f, d_f, e_f, f_f, g_f, h_f, i_f, j_f),
536            )| {
537                for _ in 0..N {
538                    (a, b, c, d, e, f, g, h, i, j) = (
539                        a * a_f,
540                        b * b_f,
541                        c * c_f,
542                        d * d_f,
543                        e * e_f,
544                        f * f_f,
545                        g * g_f,
546                        h * h_f,
547                        i * i_f,
548                        j * j_f,
549                    );
550                }
551                (a, b, c, d, e, f, g, h, i, j)
552            },
553            BatchSize::SmallInput,
554        );
555    });
556}
557
558/// Benchmarks the `exp_const_u64` implementation for a given `POWER`.
559///
560/// This function measures the throughput of the exponentiation by applying the operation
561/// to a vector of `REPS` random elements.
562pub fn benchmark_exp_const<R: PrimeCharacteristicRing + Copy, const POWER: u64, const REPS: usize>(
563    c: &mut Criterion,
564    name: &str,
565) where
566    StandardUniform: Distribution<R>,
567{
568    let mut rng = SmallRng::seed_from_u64(1);
569    let input: Vec<R> = (0..REPS).map(|_| rng.random()).collect();
570
571    c.bench_function(&format!("{name} exp_const<{POWER}>/{REPS}"), |b| {
572        b.iter_batched(
573            || input.clone(),
574            |mut data| {
575                for x in data.iter_mut() {
576                    *x = x.exp_const_u64::<POWER>();
577                }
578                black_box(data);
579            },
580            BatchSize::SmallInput,
581        );
582    });
583}
584
585pub fn benchmark_neg_latency<R: PrimeCharacteristicRing + Copy, const N: usize>(
586    c: &mut Criterion,
587    name: &str,
588) where
589    StandardUniform: Distribution<R>,
590{
591    c.bench_function(&format!("neg-latency/{N} {name}"), |b| {
592        b.iter_batched(
593            || {
594                let mut rng = SmallRng::seed_from_u64(1);
595                let mut vec = Vec::new();
596                for _ in 0..N {
597                    vec.push(rng.random::<R>());
598                }
599                vec
600            },
601            |x| x.iter().fold(R::ZERO, |acc, y| -(acc + *y)),
602            BatchSize::SmallInput,
603        );
604    });
605}
606
607pub fn benchmark_neg_throughput<R: PrimeCharacteristicRing + Copy, const N: usize>(
608    c: &mut Criterion,
609    name: &str,
610) where
611    StandardUniform: Distribution<R>,
612{
613    c.bench_function(&format!("neg-throughput/{N} {name}"), |b| {
614        b.iter_batched(
615            || {
616                let mut rng = SmallRng::seed_from_u64(1);
617                (
618                    rng.random::<R>(),
619                    rng.random::<R>(),
620                    rng.random::<R>(),
621                    rng.random::<R>(),
622                    rng.random::<R>(),
623                    rng.random::<R>(),
624                    rng.random::<R>(),
625                    rng.random::<R>(),
626                    rng.random::<R>(),
627                    rng.random::<R>(),
628                )
629            },
630            |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
631                for _ in 0..N {
632                    (a, b, c, d, e, f, g, h, i, j) = (-a, -b, -c, -d, -e, -f, -g, -h, -i, -j);
633                }
634                (a, b, c, d, e, f, g, h, i, j)
635            },
636            BatchSize::SmallInput,
637        );
638    });
639}
640
641pub fn benchmark_double_latency<R: PrimeCharacteristicRing + Copy, const N: usize>(
642    c: &mut Criterion,
643    name: &str,
644) where
645    StandardUniform: Distribution<R>,
646{
647    c.bench_function(&format!("double-latency/{N} {name}"), |b| {
648        b.iter_batched(
649            || {
650                let mut rng = SmallRng::seed_from_u64(1);
651                black_box(rng.random::<R>())
652            },
653            |x| {
654                let mut acc = x;
655                for _ in 0..N {
656                    acc = acc.double();
657                }
658                acc
659            },
660            BatchSize::SmallInput,
661        );
662    });
663}
664
665pub fn benchmark_double_throughput<R: PrimeCharacteristicRing + Copy, const N: usize>(
666    c: &mut Criterion,
667    name: &str,
668) where
669    StandardUniform: Distribution<R>,
670{
671    c.bench_function(&format!("double-throughput/{N} {name}"), |b| {
672        b.iter_batched(
673            || {
674                let mut rng = SmallRng::seed_from_u64(1);
675                (
676                    rng.random::<R>(),
677                    rng.random::<R>(),
678                    rng.random::<R>(),
679                    rng.random::<R>(),
680                    rng.random::<R>(),
681                    rng.random::<R>(),
682                    rng.random::<R>(),
683                    rng.random::<R>(),
684                    rng.random::<R>(),
685                    rng.random::<R>(),
686                )
687            },
688            |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
689                for _ in 0..N {
690                    (a, b, c, d, e, f, g, h, i, j) = (
691                        a.double(),
692                        b.double(),
693                        c.double(),
694                        d.double(),
695                        e.double(),
696                        f.double(),
697                        g.double(),
698                        h.double(),
699                        i.double(),
700                        j.double(),
701                    );
702                }
703                (a, b, c, d, e, f, g, h, i, j)
704            },
705            BatchSize::SmallInput,
706        );
707    });
708}
709
710/// Benchmark [`chunked_linear_combination`] across all candidate chunk sizes
711/// (1, 2, 4, 8, 16, 32, 64) on `LEN` elements.
712pub fn benchmark_chunked_linear_combination<F: Field, A: Algebra<F> + Copy, const LEN: usize>(
713    c: &mut Criterion,
714    name: &str,
715) where
716    StandardUniform: Distribution<F> + Distribution<A>,
717{
718    let mut rng = SmallRng::seed_from_u64(1);
719    let values: Vec<A> = (0..LEN).map(|_| rng.random()).collect();
720    let coeffs: Vec<F> = (0..LEN).map(|_| rng.random()).collect();
721
722    macro_rules! bench_chunk {
723        ($($chunk:literal),*) => {$(
724            c.bench_function(
725                &format!("{name} batched_lc/chunk={}, len={LEN}", $chunk),
726                |b| {
727                    b.iter(|| {
728                        chunked_linear_combination::<$chunk, A, F>(
729                            black_box(values.as_slice()),
730                            black_box(coeffs.as_slice()),
731                        )
732                    });
733                },
734            );
735        )*};
736    }
737    bench_chunk!(1, 2, 4, 8, 16, 32, 64);
738}
739
740/// Wire up packed-extension benchmarks for one or more `(label, ScalarEF)` pairs.
741///
742/// For each pair, generates a Criterion benchmark function that exercises
743/// add/mul/div latency and throughput on `<ScalarEF as ExtensionField<Base>>::ExtensionPacking`.
744/// Emits `criterion_group!` and `criterion_main!` at the call site, so this should be
745/// invoked from a `benches/*.rs` file as the file's top-level content.
746///
747/// # Example
748///
749/// ```ignore
750/// use p3_baby_bear::BabyBear;
751/// use p3_field::extension::BinomialExtensionField;
752/// use p3_field_testing::bench_packed_extension_field;
753///
754/// bench_packed_extension_field! {
755///     BabyBear,
756///     quartic = BinomialExtensionField<BabyBear, 4>,
757///     quintic = BinomialExtensionField<BabyBear, 5>,
758///     octic = BinomialExtensionField<BabyBear, 8>,
759/// }
760/// ```
761#[macro_export]
762macro_rules! bench_packed_extension_field {
763    ($base:ty, $($label:ident = $ef:ty),+ $(,)?) => {
764        // Each round of throughput has 10 operations; run latency tests with 10× reps.
765        const REPS: usize = 100;
766        const L_REPS: usize = 10 * REPS;
767
768        $(
769            fn $label(c: &mut criterion::Criterion) {
770                type Packed = <$ef as p3_field::ExtensionField<$base>>::ExtensionPacking;
771                let name = stringify!($ef);
772                $crate::bench_func::benchmark_add_throughput::<Packed, REPS>(c, name);
773                $crate::bench_func::benchmark_add_latency::<Packed, L_REPS>(c, name);
774                $crate::bench_func::benchmark_mul_throughput::<Packed, REPS>(c, name);
775                $crate::bench_func::benchmark_mul_latency::<Packed, L_REPS>(c, name);
776                $crate::bench_func::benchmark_div_throughput::<Packed, REPS>(c, name);
777                $crate::bench_func::benchmark_div_latency::<Packed, L_REPS>(c, name);
778            }
779        )+
780
781        criterion::criterion_group!(packed_ext_benches, $($label),+);
782        criterion::criterion_main!(packed_ext_benches);
783    };
784}