ark_ff_optimized/
macros.rs

1// My main motivation with the benchmarks here was to compare the performance of arkworks
2// generic field implementations with the specialized field implementations in this repo. I
3// originally wanted to re-use the macros defined in the "ark-algebra-bench-templates" crate
4// but Criterion's facilities for analyzing benchmarks in the aggregate didn't enable for
5// comparison. I tried to use https://github.com/BurntSushi/critcmp to compare benchmarks but
6// the reports where hard to read and looked bad. This is a more of less a copy of the
7// benchmarks in the "ark-algebra-bench-templates" crate and parametrizes the benchmarks in
8// such a way that a nice comparison table is generated in the report.
9// Demo: http://andrewmilson.com/optimized-fields/criterion/report/index.html
10#[macro_export]
11macro_rules! field_compare {
12    (prime; $test_name:expr; $mod_name:ident; $( $field:ident ),+) => {
13        mod $mod_name {
14            use super::*;
15            use ark_ff::{Field, PrimeField, UniformRand, BigInteger};
16            use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
17
18            fn bench_compare(c: &mut Criterion) {
19                const SAMPLES: usize = 1000;
20                let mut group = c.benchmark_group($test_name);
21                $(
22                    let field_name = stringify!($field);
23                    let mut rng = ark_std::test_rng();
24                    let field_elements_left = (0..SAMPLES)
25                        .map(|_| <$field>::rand(&mut rng))
26                        .collect::<Vec<_>>();
27                    let field_elements_right = (0..SAMPLES)
28                        .map(|_| <$field>::rand(&mut rng))
29                        .collect::<Vec<_>>();
30
31                    // common arithmetic
32                    let description = "Addition";
33                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
34                        let mut i = 0;
35                        b.iter(|| {
36                            i = (i + 1) % SAMPLES;
37                            field_elements_left[i] + field_elements_right[i]
38                        })
39                    });
40                    let description = "Subtraction";
41                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
42                        let mut i = 0;
43                        b.iter(|| {
44                            i = (i + 1) % SAMPLES;
45                            field_elements_left[i] - field_elements_right[i]
46                        })
47                    });
48                    let description = "Negation";
49                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
50                        let mut i = 0;
51                        b.iter(|| {
52                            i = (i + 1) % SAMPLES;
53                            -field_elements_left[i]
54                        })
55                    });
56                    let description = "Double";
57                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
58                        let mut i = 0;
59                        b.iter(|| {
60                            i = (i + 1) % SAMPLES;
61                            field_elements_left[i].double()
62                        })
63                    });
64                    let description = "Multiplication";
65                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
66                        let mut i = 0;
67                        b.iter(|| {
68                            i = (i + 1) % SAMPLES;
69                            field_elements_left[i] * field_elements_right[i]
70                        })
71                    });
72                    let description = "Square";
73                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
74                        let mut i = 0;
75                        b.iter(|| {
76                            i = (i + 1) % SAMPLES;
77                            field_elements_left[i].square()
78                        })
79                    });
80                    let description = "Inverse";
81                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
82                        let mut i = 0;
83                        b.iter(|| {
84                            i = (i + 1) % SAMPLES;
85                            field_elements_left[i].inverse().unwrap()
86                        })
87                    });
88                    let description = "Sum of products of size 2";
89                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
90                        let mut i = 0;
91                        b.iter(|| {
92                            i = (i + 1) % SAMPLES;
93                            let j = (i + 1) % SAMPLES;
94                            <$field>::sum_of_products(
95                                &[field_elements_left[i], field_elements_right[j]],
96                                &[field_elements_left[j], field_elements_right[i]],
97                            )
98                        })
99                    });
100                    let description = "Naive sum of products of size 2";
101                    group.bench_with_input(BenchmarkId::new(field_name, description), field_name, |b, _| {
102                        let mut i = 0;
103                        b.iter(|| {
104                            i = (i + 1) % SAMPLES;
105                            let j = (i + 1) % SAMPLES;
106                            field_elements_left[i] * field_elements_left[j]
107                                + field_elements_right[j] * field_elements_right[i]
108                        })
109                    });
110
111                    // common serialization
112                    let f = field_elements_left;
113                    let mut bytes = Vec::with_capacity(1000);
114                    let f_compressed = f
115                        .iter()
116                        .map(|a| {
117                            let mut bytes = Vec::with_capacity(1000);
118                            a.serialize_compressed(&mut bytes).unwrap();
119                            bytes
120                        })
121                        .collect::<Vec<_>>();
122                    let f_uncompressed = f
123                        .iter()
124                        .map(|a| {
125                            let mut bytes = Vec::with_capacity(1000);
126                            a.serialize_uncompressed(&mut bytes).unwrap();
127                            bytes
128                        })
129                        .collect::<Vec<_>>();
130                    let description = "Serialize Compressed";
131                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
132                        let mut i = 0;
133                        b.iter(|| {
134                            i = (i + 1) % SAMPLES;
135                            bytes.clear();
136                            f[i].serialize_compressed(&mut bytes).unwrap()
137                        })
138                    });
139                    let description = "Serialize Uncompressed";
140                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
141                        let mut i = 0;
142                        b.iter(|| {
143                            i = (i + 1) % SAMPLES;
144                            bytes.clear();
145                            f[i].serialize_uncompressed(&mut bytes).unwrap()
146                        })
147                    });
148                    let description = "Deserialize Compressed";
149                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
150                        let mut i = 0;
151                        b.iter(|| {
152                            i = (i + 1) % SAMPLES;
153                            bytes.clear();
154                            <$field>::deserialize_compressed(f_compressed[i].as_slice()).unwrap()
155                        })
156                    });
157                    let description = "Deserialize Compressed Unchecked";
158                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
159                        let mut i = 0;
160                        b.iter(|| {
161                            i = (i + 1) % SAMPLES;
162                            bytes.clear();
163                            <$field>::deserialize_compressed_unchecked(f_compressed[i].as_slice()).unwrap()
164                        })
165                    });
166                    let description = "Deserialize Uncompressed";
167                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
168                        let mut i = 0;
169                        b.iter(|| {
170                            i = (i + 1) % SAMPLES;
171                            bytes.clear();
172                            <$field>::deserialize_uncompressed(f_uncompressed[i].as_slice()).unwrap()
173                        })
174                    });
175                    let description = "Deserialize Uncompressed Unchecked";
176                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
177                        let mut i = 0;
178                        b.iter(|| {
179                            i = (i + 1) % SAMPLES;
180                            bytes.clear();
181                            <$field>::deserialize_uncompressed_unchecked(f_uncompressed[i].as_slice()).unwrap()
182                        })
183                    });
184
185                    // sqrt
186                    let qrs = f.iter().map(|s| s.square()).collect::<Vec<_>>();
187                    let description = "Square Root for QR";
188                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
189                        let mut i = 0;
190                        b.iter(|| {
191                            i = (i + 1) % SAMPLES;
192                            qrs[i].sqrt().unwrap()
193                        })
194                    });
195                    let description = "Legendre for QR";
196                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
197                        let mut i = 0;
198                        b.iter(|| {
199                            i = (i + 1) % SAMPLES;
200                            qrs[i].legendre()
201                        })
202                    });
203
204                    // conversions
205                    let bigints = f.iter().map(|f| f.into_bigint()).collect::<Vec<_>>();
206                    let description = "From BigInt";
207                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
208                        let mut i = 0;
209                        b.iter(|| {
210                            i = (i + 1) % SAMPLES;
211                            <$field>::from_bigint(bigints[i])
212                        })
213                    });
214                    let description = "Into BigInt";
215                    group.bench_with_input(BenchmarkId::new(field_name, description), description, |b, _| {
216                        let mut i = 0;
217                        b.iter(|| {
218                            i = (i + 1) % SAMPLES;
219                            f[i].into_bigint()
220                        })
221                    });
222                )*
223                group.finish();
224            }
225
226            criterion::criterion_group!(benches, bench_compare);
227        }
228    };
229}