1use alloc::format;
2use alloc::vec::Vec;
3use core::hint::black_box;
4
5use criterion::{BatchSize, Criterion};
6use p3_field::{Algebra, Field, PrimeCharacteristicRing, chunked_linear_combination};
7use rand::distr::StandardUniform;
8use rand::prelude::Distribution;
9use rand::rngs::SmallRng;
10use rand::{RngExt, SeedableRng};
11
12pub fn benchmark_mul<F: Field>(c: &mut Criterion, name: &str)
15where
16 StandardUniform: Distribution<F>,
17{
18 let mut rng = SmallRng::seed_from_u64(1);
19 let x = rng.random::<F>();
20 let y = rng.random::<F>();
21 c.bench_function(&format!("{name} mul"), |b| {
22 b.iter(|| black_box(black_box(x) * black_box(y)));
23 });
24}
25
26pub fn benchmark_square<F: Field>(c: &mut Criterion, name: &str)
27where
28 StandardUniform: Distribution<F>,
29{
30 let mut rng = SmallRng::seed_from_u64(1);
31 let x = rng.random::<F>();
32 c.bench_function(&format!("{name} square"), |b| {
33 b.iter(|| black_box(black_box(x).square()));
34 });
35}
36
37pub fn benchmark_inv<F: Field>(c: &mut Criterion, name: &str)
38where
39 StandardUniform: Distribution<F>,
40{
41 let mut rng = SmallRng::seed_from_u64(1);
42 let x = rng.random::<F>();
43 c.bench_function(&format!("{name} inv"), |b| {
44 b.iter(|| black_box(black_box(x)).inverse());
45 });
46}
47
48pub fn benchmark_mul_2exp<R: PrimeCharacteristicRing + Copy, const REPS: usize>(
49 c: &mut Criterion,
50 name: &str,
51 val: u64,
52) where
53 StandardUniform: Distribution<R>,
54{
55 let mut rng = SmallRng::seed_from_u64(1);
56 let mut input = Vec::new();
57 for _ in 0..REPS {
58 input.push(rng.random::<R>());
59 }
60 c.bench_function(&format!("{name} mul_2exp_u64 {val}"), |b| {
61 b.iter(|| input.iter_mut().for_each(|i| *i = i.mul_2exp_u64(val)));
62 });
63}
64
65pub fn benchmark_halve<F: Field, const REPS: usize>(c: &mut Criterion, name: &str)
66where
67 StandardUniform: Distribution<F>,
68{
69 let mut rng = SmallRng::seed_from_u64(1);
70 let mut input = Vec::new();
71 for _ in 0..REPS {
72 input.push(rng.random::<F>());
73 }
74 c.bench_function(&format!("{name} halve. Num Reps: {REPS}"), |b| {
75 b.iter(|| input.iter_mut().for_each(|i| *i = i.halve()));
76 });
77}
78
79pub fn benchmark_div_2exp<F: Field, const REPS: usize>(c: &mut Criterion, name: &str, val: u64)
80where
81 StandardUniform: Distribution<F>,
82{
83 let mut rng = SmallRng::seed_from_u64(1);
84 let mut input = Vec::new();
85 for _ in 0..REPS {
86 input.push(rng.random::<F>());
87 }
88 c.bench_function(&format!("{name} div_2exp_u64 {val}"), |b| {
89 b.iter(|| input.iter_mut().for_each(|i| *i = i.div_2exp_u64(val)));
90 });
91}
92
93pub fn benchmark_iter_sum<R: PrimeCharacteristicRing + Copy, const N: usize, const REPS: usize>(
99 c: &mut Criterion,
100 name: &str,
101) where
102 StandardUniform: Distribution<R>,
103{
104 let mut rng = SmallRng::seed_from_u64(1);
105 let mut input = Vec::new();
106 for _ in 0..REPS {
107 input.push(rng.random::<[R; N]>());
108 }
109 c.bench_function(&format!("{name} sum/{REPS}, {N}"), |b| {
110 b.iter(|| {
111 let mut acc = R::ZERO;
112 for row in &mut input {
113 acc += row.iter().copied().sum();
114 }
115 acc
116 });
117 });
118}
119
120pub fn benchmark_sum_array<R: PrimeCharacteristicRing + Copy, const N: usize, const REPS: usize>(
126 c: &mut Criterion,
127 name: &str,
128) where
129 StandardUniform: Distribution<R>,
130{
131 let mut rng = SmallRng::seed_from_u64(1);
132 let mut input = Vec::new();
133 for _ in 0..REPS {
134 input.push(rng.random::<[R; N]>());
135 }
136 c.bench_function(&format!("{name} tree sum/{REPS}, {N}"), |b| {
137 b.iter(|| {
138 let mut acc = R::ZERO;
139 for row in &mut input {
140 acc += R::sum_array::<N>(row);
141 }
142 acc
143 });
144 });
145}
146
147pub fn benchmark_dot_array<R: PrimeCharacteristicRing + Copy, const N: usize>(
152 c: &mut Criterion,
153 name: &str,
154) where
155 StandardUniform: Distribution<R>,
156{
157 let mut rng = SmallRng::seed_from_u64(1);
158 let lhs = rng.random::<[R; N]>();
159 let rhs = rng.random::<[R; N]>();
160
161 c.bench_function(&format!("{name} dot product/{N}"), |b| {
162 b.iter(|| black_box(R::dot_product(black_box(&lhs), black_box(&rhs))));
163 });
164}
165
166pub fn benchmark_mixed_dot_array<A: Algebra<F> + Copy, F: Field, const N: usize>(
168 c: &mut Criterion,
169 name: &str,
170) where
171 StandardUniform: Distribution<A> + Distribution<F>,
172{
173 let mut rng = SmallRng::seed_from_u64(1);
174 let a = rng.random::<[A; N]>();
175 let f = rng.random::<[F; N]>();
176 c.bench_function(&format!("{name} mixed dot product/{N}"), |b| {
177 b.iter(|| black_box(A::mixed_dot_product(black_box(&a), black_box(&f))));
178 });
179}
180
181pub fn benchmark_add_slices<F: Field, const LENGTH: usize>(c: &mut Criterion, name: &str)
183where
184 StandardUniform: Distribution<F>,
185{
186 let mut rng = SmallRng::seed_from_u64(1);
187 let mut slice_1 = Vec::new();
188 let mut slice_2 = Vec::new();
189 for _ in 0..LENGTH {
190 slice_1.push(rng.random());
191 slice_2.push(rng.random());
192 }
193 c.bench_function(&format!("{name} add slices/{LENGTH}"), |b| {
194 let mut in_slice = slice_1.clone();
195 b.iter(|| {
196 F::add_slices(&mut in_slice, &slice_2);
197 });
198 });
199}
200
201pub fn benchmark_add_latency<R: PrimeCharacteristicRing + Copy, const N: usize>(
202 c: &mut Criterion,
203 name: &str,
204) where
205 StandardUniform: Distribution<R>,
206{
207 c.bench_function(&format!("add-latency/{N} {name}"), |b| {
208 b.iter_batched(
209 || {
210 let mut rng = SmallRng::seed_from_u64(1);
211 let mut vec = Vec::new();
212 for _ in 0..N {
213 vec.push(rng.random::<R>());
214 }
215 vec
216 },
217 |x| x.iter().fold(R::ZERO, |x, y| x + *y),
218 BatchSize::SmallInput,
219 );
220 });
221}
222
223pub fn benchmark_add_throughput<R: PrimeCharacteristicRing + Copy, const N: usize>(
224 c: &mut Criterion,
225 name: &str,
226) where
227 StandardUniform: Distribution<R>,
228{
229 c.bench_function(&format!("add-throughput/{N} {name}"), |b| {
230 b.iter_batched(
231 || {
232 let mut rng = SmallRng::seed_from_u64(1);
233 (
234 rng.random::<R>(),
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 )
245 },
246 |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
247 for _ in 0..N {
248 (a, b, c, d, e, f, g, h, i, j) = (
249 a + b,
250 b + c,
251 c + d,
252 d + e,
253 e + f,
254 f + g,
255 g + h,
256 h + i,
257 i + j,
258 j + a,
259 );
260 }
261 (a, b, c, d, e, f, g, h, i, j)
262 },
263 BatchSize::SmallInput,
264 );
265 });
266}
267
268pub fn benchmark_sub_latency<R: PrimeCharacteristicRing + Copy, const N: usize>(
269 c: &mut Criterion,
270 name: &str,
271) where
272 StandardUniform: Distribution<R>,
273{
274 c.bench_function(&format!("sub-latency/{N} {name}"), |b| {
275 b.iter_batched(
276 || {
277 let mut rng = SmallRng::seed_from_u64(1);
278 let mut vec = Vec::new();
279 for _ in 0..N {
280 vec.push(rng.random::<R>());
281 }
282 vec
283 },
284 |x| x.iter().fold(R::ZERO, |x, y| x - *y),
285 BatchSize::SmallInput,
286 );
287 });
288}
289
290pub fn benchmark_sub_throughput<R: PrimeCharacteristicRing + Copy, const N: usize>(
291 c: &mut Criterion,
292 name: &str,
293) where
294 StandardUniform: Distribution<R>,
295{
296 c.bench_function(&format!("sub-throughput/{N} {name}"), |b| {
297 b.iter_batched(
298 || {
299 let mut rng = SmallRng::seed_from_u64(1);
300 (
301 rng.random::<R>(),
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 )
312 },
313 |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
314 for _ in 0..N {
315 (a, b, c, d, e, f, g, h, i, j) = (
316 a - b,
317 b - c,
318 c - d,
319 d - e,
320 e - f,
321 f - g,
322 g - h,
323 h - i,
324 i - j,
325 j - a,
326 );
327 }
328 (a, b, c, d, e, f, g, h, i, j)
329 },
330 BatchSize::SmallInput,
331 );
332 });
333}
334
335pub fn benchmark_mul_latency<R: PrimeCharacteristicRing + Copy, const N: usize>(
336 c: &mut Criterion,
337 name: &str,
338) where
339 StandardUniform: Distribution<R>,
340{
341 c.bench_function(&format!("mul-latency/{N} {name}"), |b| {
342 b.iter_batched(
343 || {
344 let mut rng = SmallRng::seed_from_u64(1);
345 let mut vec = Vec::new();
346 for _ in 0..N {
347 vec.push(rng.random::<R>());
348 }
349 vec
350 },
351 |x| x.iter().fold(R::ONE, |x, y| x * *y),
352 BatchSize::SmallInput,
353 );
354 });
355}
356
357pub fn benchmark_mul_throughput<R: PrimeCharacteristicRing + Copy, const N: usize>(
358 c: &mut Criterion,
359 name: &str,
360) where
361 StandardUniform: Distribution<R>,
362{
363 c.bench_function(&format!("mul-throughput/{N} {name}"), |b| {
364 b.iter_batched(
365 || {
366 let mut rng = SmallRng::seed_from_u64(1);
367 (
368 rng.random::<R>(),
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 )
379 },
380 |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
381 for _ in 0..N {
382 (a, b, c, d, e, f, g, h, i, j) = (
383 a * b,
384 b * c,
385 c * d,
386 d * e,
387 e * f,
388 f * g,
389 g * h,
390 h * i,
391 i * j,
392 j * a,
393 );
394 }
395 (a, b, c, d, e, f, g, h, i, j)
396 },
397 BatchSize::SmallInput,
398 );
399 });
400}
401
402pub fn benchmark_base_mul_latency<F: Field, A: Algebra<F> + Copy, const N: usize>(
403 c: &mut Criterion,
404 name: &str,
405) where
406 StandardUniform: Distribution<F> + Distribution<A>,
407{
408 c.bench_function(&format!("base_mul-latency/{N} {name}"), |b| {
409 b.iter_batched(
410 || {
411 let mut rng = SmallRng::seed_from_u64(1);
412 let mut vec = Vec::new();
413 for _ in 0..N {
414 vec.push(rng.random::<F>());
415 }
416 let init_val = rng.random::<A>();
417 (vec, init_val)
418 },
419 |(x, init_val)| x.iter().fold(init_val, |x, y| x * *y),
420 BatchSize::SmallInput,
421 );
422 });
423}
424
425pub fn benchmark_base_mul_throughput<F: Field, A: Algebra<F> + Copy, const N: usize>(
426 c: &mut Criterion,
427 name: &str,
428) where
429 StandardUniform: Distribution<F> + Distribution<A>,
430{
431 c.bench_function(&format!("base_mul-throughput/{N} {name}"), |b| {
432 b.iter_batched(
433 || {
434 let mut rng = SmallRng::seed_from_u64(1);
435 let a_tuple = (
436 rng.random::<A>(),
437 rng.random::<A>(),
438 rng.random::<A>(),
439 rng.random::<A>(),
440 rng.random::<A>(),
441 rng.random::<A>(),
442 rng.random::<A>(),
443 rng.random::<A>(),
444 rng.random::<A>(),
445 rng.random::<A>(),
446 );
447 let f_tuple = (
448 rng.random::<F>(),
449 rng.random::<F>(),
450 rng.random::<F>(),
451 rng.random::<F>(),
452 rng.random::<F>(),
453 rng.random::<F>(),
454 rng.random::<F>(),
455 rng.random::<F>(),
456 rng.random::<F>(),
457 rng.random::<F>(),
458 );
459 (a_tuple, f_tuple)
460 },
461 |(
462 (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j),
463 (a_f, b_f, c_f, d_f, e_f, f_f, g_f, h_f, i_f, j_f),
464 )| {
465 for _ in 0..N {
466 (a, b, c, d, e, f, g, h, i, j) = (
467 a * a_f,
468 b * b_f,
469 c * c_f,
470 d * d_f,
471 e * e_f,
472 f * f_f,
473 g * g_f,
474 h * h_f,
475 i * i_f,
476 j * j_f,
477 );
478 }
479 (a, b, c, d, e, f, g, h, i, j)
480 },
481 BatchSize::SmallInput,
482 );
483 });
484}
485
486pub fn benchmark_exp_const<R: PrimeCharacteristicRing + Copy, const POWER: u64, const REPS: usize>(
491 c: &mut Criterion,
492 name: &str,
493) where
494 StandardUniform: Distribution<R>,
495{
496 let mut rng = SmallRng::seed_from_u64(1);
497 let input: Vec<R> = (0..REPS).map(|_| rng.random()).collect();
498
499 c.bench_function(&format!("{name} exp_const<{POWER}>/{REPS}"), |b| {
500 b.iter_batched(
501 || input.clone(),
502 |mut data| {
503 for x in data.iter_mut() {
504 *x = x.exp_const_u64::<POWER>();
505 }
506 black_box(data);
507 },
508 BatchSize::SmallInput,
509 );
510 });
511}
512
513pub fn benchmark_neg_latency<R: PrimeCharacteristicRing + Copy, const N: usize>(
514 c: &mut Criterion,
515 name: &str,
516) where
517 StandardUniform: Distribution<R>,
518{
519 c.bench_function(&format!("neg-latency/{N} {name}"), |b| {
520 b.iter_batched(
521 || {
522 let mut rng = SmallRng::seed_from_u64(1);
523 let mut vec = Vec::new();
524 for _ in 0..N {
525 vec.push(rng.random::<R>());
526 }
527 vec
528 },
529 |x| x.iter().fold(R::ZERO, |acc, y| -(acc + *y)),
530 BatchSize::SmallInput,
531 );
532 });
533}
534
535pub fn benchmark_neg_throughput<R: PrimeCharacteristicRing + Copy, const N: usize>(
536 c: &mut Criterion,
537 name: &str,
538) where
539 StandardUniform: Distribution<R>,
540{
541 c.bench_function(&format!("neg-throughput/{N} {name}"), |b| {
542 b.iter_batched(
543 || {
544 let mut rng = SmallRng::seed_from_u64(1);
545 (
546 rng.random::<R>(),
547 rng.random::<R>(),
548 rng.random::<R>(),
549 rng.random::<R>(),
550 rng.random::<R>(),
551 rng.random::<R>(),
552 rng.random::<R>(),
553 rng.random::<R>(),
554 rng.random::<R>(),
555 rng.random::<R>(),
556 )
557 },
558 |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
559 for _ in 0..N {
560 (a, b, c, d, e, f, g, h, i, j) = (-a, -b, -c, -d, -e, -f, -g, -h, -i, -j);
561 }
562 (a, b, c, d, e, f, g, h, i, j)
563 },
564 BatchSize::SmallInput,
565 );
566 });
567}
568
569pub fn benchmark_double_latency<R: PrimeCharacteristicRing + Copy, const N: usize>(
570 c: &mut Criterion,
571 name: &str,
572) where
573 StandardUniform: Distribution<R>,
574{
575 c.bench_function(&format!("double-latency/{N} {name}"), |b| {
576 b.iter_batched(
577 || {
578 let mut rng = SmallRng::seed_from_u64(1);
579 black_box(rng.random::<R>())
580 },
581 |x| {
582 let mut acc = x;
583 for _ in 0..N {
584 acc = acc.double();
585 }
586 acc
587 },
588 BatchSize::SmallInput,
589 );
590 });
591}
592
593pub fn benchmark_double_throughput<R: PrimeCharacteristicRing + Copy, const N: usize>(
594 c: &mut Criterion,
595 name: &str,
596) where
597 StandardUniform: Distribution<R>,
598{
599 c.bench_function(&format!("double-throughput/{N} {name}"), |b| {
600 b.iter_batched(
601 || {
602 let mut rng = SmallRng::seed_from_u64(1);
603 (
604 rng.random::<R>(),
605 rng.random::<R>(),
606 rng.random::<R>(),
607 rng.random::<R>(),
608 rng.random::<R>(),
609 rng.random::<R>(),
610 rng.random::<R>(),
611 rng.random::<R>(),
612 rng.random::<R>(),
613 rng.random::<R>(),
614 )
615 },
616 |(mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h, mut i, mut j)| {
617 for _ in 0..N {
618 (a, b, c, d, e, f, g, h, i, j) = (
619 a.double(),
620 b.double(),
621 c.double(),
622 d.double(),
623 e.double(),
624 f.double(),
625 g.double(),
626 h.double(),
627 i.double(),
628 j.double(),
629 );
630 }
631 (a, b, c, d, e, f, g, h, i, j)
632 },
633 BatchSize::SmallInput,
634 );
635 });
636}
637
638pub fn benchmark_chunked_linear_combination<F: Field, A: Algebra<F> + Copy, const LEN: usize>(
641 c: &mut Criterion,
642 name: &str,
643) where
644 StandardUniform: Distribution<F> + Distribution<A>,
645{
646 let mut rng = SmallRng::seed_from_u64(1);
647 let values: Vec<A> = (0..LEN).map(|_| rng.random()).collect();
648 let coeffs: Vec<F> = (0..LEN).map(|_| rng.random()).collect();
649
650 macro_rules! bench_chunk {
651 ($($chunk:literal),*) => {$(
652 c.bench_function(
653 &format!("{name} batched_lc/chunk={}, len={LEN}", $chunk),
654 |b| {
655 b.iter(|| {
656 chunked_linear_combination::<$chunk, A, F>(
657 black_box(values.as_slice()),
658 black_box(coeffs.as_slice()),
659 )
660 });
661 },
662 );
663 )*};
664 }
665 bench_chunk!(1, 2, 4, 8, 16, 32, 64);
666}