Skip to content
Snippets Groups Projects
Commit 4dcfdfb0 authored by STEVAN Antoine's avatar STEVAN Antoine :crab:
Browse files

measure atomic operations of curve groups and prime fields (dragoon/komodo!78)

this MR adds two now benchmarks:
- `field_operations` in `benches/operations/field.rs`
- `curve_group_operations` in `benches/operations/curve_group.rs`

as well as `scripts/plot/multi_bar.py` to plot the results, see `benches/README.md` for the commands to run.

## results
![curve_group](/uploads/0a27dcdc7965090b0429867e1822a40c/curve_group.png)

![field](/uploads/461455568a0a637f78f9c2b6d1a68f59/field.png)
parent 7c6f46dc
No related branches found
No related tags found
No related merge requests found
...@@ -65,6 +65,16 @@ harness = false ...@@ -65,6 +65,16 @@ harness = false
name = "commit" name = "commit"
harness = false harness = false
[[bench]]
name = "field_operations"
path = "benches/operations/field.rs"
harness = false
[[bench]]
name = "curve_group_operations"
path = "benches/operations/curve_group.rs"
harness = false
[[example]] [[example]]
name = "bench_commit" name = "bench_commit"
path = "examples/benches/commit.rs" path = "examples/benches/commit.rs"
......
...@@ -14,11 +14,34 @@ python scripts/plot/benches.py results.ndjson --bench linalg ...@@ -14,11 +14,34 @@ python scripts/plot/benches.py results.ndjson --bench linalg
python scripts/plot/benches.py results.ndjson --bench setup python scripts/plot/benches.py results.ndjson --bench setup
``` ```
## atomic operations
```nushell
cargo criterion --output-format verbose --message-format json --bench field_operations out> field.ndjson
cargo criterion --output-format verbose --message-format json --bench curve_group_operations out> curve.ndjson
```
```nushell
def read-atomic-ops []: list -> record {
where reason == "benchmark-complete"
| select id mean.estimate
| rename --column { mean_estimate: "mean" }
| sort-by id
| update id { parse "{op} on {curve}" }
| flatten --all
| group-by op --to-table
| reject items.op
| update items { transpose -r | into record }
| transpose -r
| into record
}
python scripts/plot/multi_bar.py (open field.ndjson | read-atomic-ops | to json) --title "field operations" -l "time (in ns)"
python scripts/plot/multi_bar.py (open curve.ndjson | read-atomic-ops | to json) --title "curve group operations" -l "time (in ns)"
```
## oneshot benchmarks ## oneshot benchmarks
these are benchmarks that run a single measurement, implemented as _examples_ in these are benchmarks that run a single measurement, implemented as _examples_ in
`examples/benches/`. `examples/benches/`.
## commit ### commit
```nushell ```nushell
let res = cargo run --example bench_commit let res = cargo run --example bench_commit
| lines | lines
......
// see `benches/README.md`
use std::time::Duration;
use ark_ec::CurveGroup;
use ark_ff::PrimeField;
use criterion::{criterion_group, criterion_main, Criterion};
fn random_sampling_template<G: CurveGroup>(c: &mut Criterion, curve: &str) {
let mut rng = ark_std::test_rng();
c.bench_function(&format!("random sampling on {}", curve), |b| {
b.iter(|| {
let _ = G::rand(&mut rng);
});
});
}
fn group_template<F: PrimeField, G: CurveGroup<ScalarField = F>>(c: &mut Criterion, curve: &str) {
let mut rng = ark_std::test_rng();
c.bench_function(&format!("addition on {}", curve), |b| {
b.iter(|| {
let g1 = G::rand(&mut rng);
let g2 = G::rand(&mut rng);
g1 + g2
});
});
c.bench_function(&format!("substraction on {}", curve), |b| {
b.iter(|| {
let g1 = G::rand(&mut rng);
let g2 = G::rand(&mut rng);
g1 - g2
});
});
c.bench_function(&format!("double on {}", curve), |b| {
b.iter(|| {
let g1 = G::rand(&mut rng);
g1.double()
});
});
c.bench_function(&format!("scalar multiplication on {}", curve), |b| {
b.iter(|| {
let g1 = G::rand(&mut rng);
let f1 = F::rand(&mut rng);
g1.mul(f1)
});
});
}
fn curve_group_template<F: PrimeField, G: CurveGroup<ScalarField = F>>(
c: &mut Criterion,
curve: &str,
) {
let mut rng = ark_std::test_rng();
c.bench_function(&format!("into affine on {}", curve), |b| {
b.iter(|| {
let g1 = G::rand(&mut rng);
g1.into_affine()
});
});
c.bench_function(&format!("from affine on {}", curve), |b| {
b.iter(|| {
let g1_affine = G::rand(&mut rng).into_affine();
let _: G = g1_affine.into();
});
});
c.bench_function(&format!("affine addition on {}", curve), |b| {
b.iter(|| {
let g1_affine = G::rand(&mut rng).into_affine();
let g2_affine = G::rand(&mut rng).into_affine();
g1_affine + g2_affine
});
});
c.bench_function(&format!("affine scalar multiplication on {}", curve), |b| {
b.iter(|| {
let g1_affine = G::rand(&mut rng).into_affine();
let f1 = F::rand(&mut rng);
g1_affine * f1
});
});
}
macro_rules! bench {
($c:ident, $b:ident, G1=$g:ident, name=$n:expr) => {
random_sampling_template::<$c::$g>($b, $n);
group_template::<$c::Fr, $c::$g>($b, $n);
curve_group_template::<$c::Fr, $c::$g>($b, $n);
};
}
fn bench(c: &mut Criterion) {
bench!(ark_bls12_381, c, G1 = G1Projective, name = "BLS12-381");
bench!(ark_bn254, c, G1 = G1Projective, name = "BN-254");
bench!(ark_pallas, c, G1 = Projective, name = "PALLAS");
}
criterion_group!(
name = benches;
config = Criterion::default()
.warm_up_time(Duration::from_secs_f32(0.5))
.sample_size(10);
targets = bench
);
criterion_main!(benches);
// see `benches/README.md`
use std::time::Duration;
use ark_ff::PrimeField;
use criterion::{criterion_group, criterion_main, Criterion};
fn random_sampling_template<F: PrimeField>(c: &mut Criterion, curve: &str) {
let mut rng = ark_std::test_rng();
c.bench_function(&format!("random sampling on {}", curve), |b| {
b.iter(|| {
let _ = F::rand(&mut rng);
});
});
}
fn additive_group_template<F: PrimeField>(c: &mut Criterion, curve: &str) {
let mut rng = ark_std::test_rng();
c.bench_function(&format!("addition on {}", curve), |b| {
b.iter(|| {
let f1 = F::rand(&mut rng);
let f2 = F::rand(&mut rng);
f1 + f2
});
});
c.bench_function(&format!("substraction on {}", curve), |b| {
b.iter(|| {
let f1 = F::rand(&mut rng);
let f2 = F::rand(&mut rng);
f1 - f2
});
});
c.bench_function(&format!("double on {}", curve), |b| {
b.iter(|| {
let f1 = F::rand(&mut rng);
f1.double()
});
});
c.bench_function(&format!("multiplication on {}", curve), |b| {
b.iter(|| {
let f1 = F::rand(&mut rng);
let f2 = F::rand(&mut rng);
f1 * f2
});
});
}
fn field_template<F: PrimeField>(c: &mut Criterion, curve: &str) {
let mut rng = ark_std::test_rng();
c.bench_function(&format!("square on {}", curve), |b| {
b.iter(|| {
let f1 = F::rand(&mut rng);
f1.square()
});
});
c.bench_function(&format!("inverse on {}", curve), |b| {
b.iter(|| {
let f1 = F::rand(&mut rng);
f1.inverse()
});
});
c.bench_function(&format!("legendre on {}", curve), |b| {
b.iter(|| {
let f1 = F::rand(&mut rng);
f1.legendre()
});
});
c.bench_function(&format!("sqrt on {}", curve), |b| {
b.iter(|| {
let f1 = F::rand(&mut rng);
if f1.legendre().is_qr() {
let _ = f1.sqrt();
}
});
});
}
fn prime_field_template<F: PrimeField>(c: &mut Criterion, curve: &str) {
let mut rng = ark_std::test_rng();
c.bench_function(&format!("exponentiation on {}", curve), |b| {
b.iter(|| {
let f1 = F::rand(&mut rng);
f1.pow(F::MODULUS)
});
});
}
fn conversions_template<F: PrimeField>(c: &mut Criterion, curve: &str) {
let mut rng = ark_std::test_rng();
c.bench_function(&format!("bigint conversion on {}", curve), |b| {
b.iter(|| {
let f1 = F::rand(&mut rng);
f1.into_bigint()
});
});
}
macro_rules! bench {
($c:ident, $b:ident, F=$f:ident, name=$n:expr) => {
random_sampling_template::<$c::$f>($b, $n);
additive_group_template::<$c::$f>($b, $n);
field_template::<$c::$f>($b, $n);
prime_field_template::<$c::$f>($b, $n);
conversions_template::<$c::$f>($b, $n);
};
}
fn bench(c: &mut Criterion) {
bench!(ark_bls12_381, c, F = Fr, name = "BLS12-381");
bench!(ark_bn254, c, F = Fr, name = "BN-254");
bench!(ark_pallas, c, F = Fr, name = "PALLAS");
}
criterion_group!(
name = benches;
config = Criterion::default()
.warm_up_time(Duration::from_secs_f32(0.5))
.sample_size(10);
targets = bench
);
criterion_main!(benches);
# see `benches/README.md`
from typing import Dict, List
import matplotlib.pyplot as plt
import numpy as np
import json
import sys
import argparse
# convert raw data into groups and measurements
#
# # Example
# input:
# ```json
# {
# "age": {
# "alice": 31,
# "bob": 28,
# "charlie": 44
# },
# "height": {
# "alice": 1.50,
# "bob": 1.82,
# "charlie": 1.65
# },
# "weight": {
# "alice": 65.3,
# "bob": 98.1,
# "charlie": 68.7
# }
# }
# ```
#
# output:
# ```
# groups = ["age", "height", "weight"]
# measurements = {
# "alice": [1.50, 31, 65.3],
# "bob": [1.82, 28, 98.1],
# "charlie": [1.65, 44, 68.7]
# }
# ```
def extract(data: Dict[str, Dict[str, float]]) -> (List[str], Dict[str, List[float]]):
groups = list(data.keys())
measurements = {}
for x in data[groups[0]].keys():
measurements[x] = [data[g][x] for g in groups]
return (groups, measurements)
# plot multi bars
#
# # Example
# input can be the output of [`extract`]
def plot_multi_bar(
groups: List[str],
measurements: Dict[str, List[float]],
title: str,
y_label: str,
labels_locations: List[float] = None,
width: float = 0.25,
nb_legend_cols: int = 3,
legend_loc: str = "upper left",
plot_layout: str = "constrained",
save: str = None,
):
if labels_locations is None:
labels_locations = np.arange(len(groups))
fig, ax = plt.subplots(layout=plot_layout)
multiplier = 0
for attribute, measurement in measurements.items():
offset = width * multiplier
rects = ax.bar(labels_locations + offset, measurement, width, label=attribute)
ax.bar_label(rects, padding=3)
multiplier += 1
ax.set_ylabel(y_label)
ax.set_title(title)
ax.set_xticks(labels_locations + width, groups)
ax.legend(loc=legend_loc, ncols=nb_legend_cols)
if save is not None:
fig.set_size_inches((16, 9), forward=False)
fig.savefig(save, dpi=500)
print(f"plot saved as `{save}`")
else:
plt.show()
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("data", type=str, help="""
the actual data to show in a multibar plot, here is an example:
{
"age": {
"alice": 31,
"bob": 28,
"charlie": 44
},
"height": {
"alice": 1.50,
"bob": 1.82,
"charlie": 1.65
},
"weight": {
"alice": 65.3,
"bob": 98.1,
"charlie": 68.7
}
}
""")
parser.add_argument("--title", "-t", type=str, help="the title of the multibar plot")
parser.add_argument("--label", "-l", type=str, help="the measurement label of the multibar plot")
parser.add_argument("--save", "-s", type=str, help="a path to save the figure to")
args = parser.parse_args()
groups, measurements = extract(json.loads(args.data))
plot_multi_bar(groups, measurements, args.title, args.label, save=args.save)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment