diff --git a/Cargo.toml b/Cargo.toml index 31e8a7330f2dd2fc325730376d532fc8f9e818b5..df73a393782fb60769b8eb6d8295ef546fb7b6fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,18 +55,6 @@ criterion = "0.3" name = "recoding" harness = false -[[bench]] -name = "linalg" -harness = false - -[[bench]] -name = "setup" -harness = false - -[[bench]] -name = "commit" -harness = false - [[example]] name = "bench_commit" path = "examples/benches/commit.rs" @@ -78,9 +66,15 @@ path = "examples/benches/setup_size.rs" [[example]] name = "bench_field_operations" path = "examples/benches/operations/field.rs" -harness = false [[example]] name = "bench_curve_group_operations" path = "examples/benches/operations/curve_group.rs" -harness = false + +[[example]] +name = "bench_setup" +path = "examples/benches/setup.rs" + +[[example]] +name = "bench_linalg" +path = "examples/benches/linalg.rs" diff --git a/benches/README.md b/benches/README.md index b9df21b961262ff72631674c32e345c33eb0ab71..93be82238e07a343c819e31d4f690e08017a6c87 100644 --- a/benches/README.md +++ b/benches/README.md @@ -1,57 +1,19 @@ -## run the benchmarks -```shell -nushell> cargo criterion --output-format verbose --message-format json out> results.ndjson -``` - -## add the _trusted setup_ sizes -```shell -nushell> cargo run --example bench_setup_size out>> results.ndjson -``` - -## plot the results -```shell -python scripts/plot/benches.py results.ndjson --bench linalg -python scripts/plot/benches.py results.ndjson --bench setup -``` - ## atomic operations ```nushell cargo run --example bench_field_operations -- --nb-measurements 1000 | lines | each { from json } - | to ndjson + | to ndjson # NOTE: see https://github.com/nushell/nushell/issues/12655 | save --force field.ndjson cargo run --example bench_curve_group_operations -- --nb-measurements 1000 | lines | each { from json } - | to ndjson + | to ndjson # NOTE: see https://github.com/nushell/nushell/issues/12655 | save --force curve_group.ndjson ``` ```nushell -def read-atomic-ops [ - --include: list<string> = [], --exclude: list<string> = [] -]: list -> record { - let raw = $in - | insert t {|it| $it.times |math avg} - | reject times - | rename --column { label: "group", name: "species", t: "measurement" } +use scripts/parse.nu read-atomic-ops - let included = if $include != [] { - $raw | where group in $include - } else { - $raw - } - - $included - | where group not-in $exclude - | group-by group --to-table - | reject items.group - | update items { transpose -r | into record } - | transpose -r - | into record -} -``` -```nushell python scripts/plot/multi_bar.py --title "simple field operations" -l "time (in ns)" ( open field.ndjson | read-atomic-ops --exclude [ "exponentiation", "legendre", "inverse", "sqrt" ] @@ -74,23 +36,105 @@ python scripts/plot/multi_bar.py --title "complex curve group operations" -l "ti ) ``` -## oneshot benchmarks -these are benchmarks that run a single measurement, implemented as _examples_ in -`examples/benches/`. +## linear algebra +```nushell +let sizes = seq 0 7 | each { 2 ** $in } +cargo run --example bench_linalg -- --nb-measurements 10 ...$sizes + | lines + | each { from json } + | to ndjson # NOTE: see https://github.com/nushell/nushell/issues/12655 + | save --force linalg.ndjson +``` +```nushell +let linalg = open linalg.ndjson + | update times { each { $in / 1_000_000 } } + | insert mean {|it| $it.times | math avg} + | insert stddev {|it| $it.times | into float | math stddev} + | update label { parse "{op} {n}"} + | flatten --all label + | into int n + +for graph in [ + [op, title]; + + ["inverse", "time to inverse an nxn matrix on certain curves"], + ["transpose", "time to transpose an nxn matrix on certain curves"], + ["mul", "time to multiply two nxn matrices on certain curves"] +] { + python scripts/plot/plot.py ...[ + --title $graph.title + --x-label "size" + --y-label "time (in ms)" + ( + $linalg + | where op == $graph.op + | rename --column { n: "x", name: "curve", mean: "measurement", stddev: "error" } + | group-by curve --to-table + | update items { reject curve } + | to json + ) + ] +} +``` + +## trusted setup +```nushell +let degrees = seq 0 13 | each { 2 ** $in } +cargo run --example bench_setup -- --nb-measurements 10 ...$degrees + | lines + | each { from json } + | to ndjson # NOTE: see https://github.com/nushell/nushell/issues/12655 + | save --force setup.ndjson +``` +```nushell +python scripts/plot/plot.py ...[ + --title "time to create trusted setups for certain curves" + --x-label "degree" + --y-label "time (in ms)" + ( + open setup.ndjson + | update times { each { $in / 1_000_000 } } + | insert mean {|it| $it.times | math avg} + | insert stddev {|it| $it.times | into float | math stddev} + | insert degree { get label | parse "degree {d}" | into record | get d | into int} + | insert curve {|it| if ($it.name | str starts-with "ARK") { + let c = $it.name | parse "ARK setup on {curve}" | into record | get curve + $"($c)-ark" + } else { + $it.name | parse "setup on {curve}" | into record | get curve + }} + | rename --column { degree: "x", mean: "measurement", stddev: "error" } + | group-by curve --to-table + | update items { reject curve } + | to json + ) +] +``` -### commit +## commit ```nushell let degrees = seq 0 15 | each { 2 ** $in } -let res = cargo run --example bench_commit -- --nb-measurements 10 ...$degrees +cargo run --example bench_commit -- --nb-measurements 10 ...$degrees | lines | each { from nuon } - | update times { into duration } - | insert mean {|it| $it.times | math avg} - | insert stddev {|it| $it.times | into int | into float | math stddev | into int | into duration} - | update label { parse "degree {d}" | into record | get d | into int } - | rename --column { label: "degree", name: "curve" } - -python scripts/plot/bench_commit.py ( - $res | group-by curve --to-table | update items { reject curve } | to json -) + | to ndjson # NOTE: see https://github.com/nushell/nushell/issues/12655 + | save --force commit.ndjson +``` +```nushell +python scripts/plot/plot.py ...[ + --title "time to commit polynomials for certain curves" + --x-label "degree" + --y-label "time (in ms)" + ( + open commit.ndjson + | update times { each { $in / 1_000_000 } } + | insert mean {|it| $it.times | math avg} + | insert stddev {|it| $it.times | into float | math stddev} + | update label { parse "degree {d}" | into record | get d | into int } + | rename --column { label: "x", name: "curve", mean: "measurement", stddev: "error" } + | group-by curve --to-table + | update items { reject curve } + | to json + ) +] ``` diff --git a/benches/commit.rs b/benches/commit.rs deleted file mode 100644 index 21f59cbf582577552c11ec4065b008ef86b133e4..0000000000000000000000000000000000000000 --- a/benches/commit.rs +++ /dev/null @@ -1,87 +0,0 @@ -// see `benches/README.md` -use std::time::Duration; - -use ark_ec::{pairing::Pairing, CurveGroup}; -use ark_ff::PrimeField; -use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; -use ark_poly_commit::kzg10::{Powers, KZG10}; -use ark_std::ops::Div; - -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -use komodo::zk; - -fn commit_template<F, G, P>(c: &mut Criterion, degree: usize, curve: &str) -where - F: PrimeField, - G: CurveGroup<ScalarField = F>, - P: DenseUVPolynomial<F>, - for<'a, 'b> &'a P: Div<&'b P, Output = P>, -{ - let rng = &mut rand::thread_rng(); - - let setup = zk::setup::<F, G>(degree, rng).unwrap(); - let polynomial = P::rand(degree, rng); - - c.bench_function(&format!("commit (komodo) {} on {}", degree, curve), |b| { - b.iter(|| zk::commit(&setup, &polynomial)) - }); -} - -fn ark_commit_template<E, P>(c: &mut Criterion, degree: usize, curve: &str) -where - E: Pairing, - P: DenseUVPolynomial<E::ScalarField>, - for<'a, 'b> &'a P: Div<&'b P, Output = P>, -{ - let rng = &mut rand::thread_rng(); - - let setup = KZG10::<E, P>::setup(degree, false, rng).unwrap(); - let powers_of_g = setup.powers_of_g[..=degree].to_vec(); - let powers_of_gamma_g = (0..=degree).map(|i| setup.powers_of_gamma_g[&i]).collect(); - let powers = Powers::<E> { - powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), - powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), - }; - let polynomial = P::rand(degree, rng); - - c.bench_function(&format!("commit (arkworks) {} on {}", degree, curve), |b| { - b.iter(|| KZG10::commit(&powers, &polynomial, None, None)) - }); -} - -fn commit(c: &mut Criterion) { - fn aux<F: PrimeField, G: CurveGroup<ScalarField = F>>( - c: &mut Criterion, - degree: usize, - curve: &str, - ) { - commit_template::<F, G, DensePolynomial<F>>(c, black_box(degree), curve); - } - - for n in [1, 2, 4, 8, 16] { - aux::<ark_bls12_381::Fr, ark_bls12_381::G1Projective>(c, n, "BLS12-381"); - aux::<ark_bn254::Fr, ark_bn254::G1Projective>(c, n, "BN-254"); - aux::<ark_pallas::Fr, ark_pallas::Projective>(c, n, "PALLAS"); - } -} - -fn ark_commit(c: &mut Criterion) { - fn aux<E: Pairing>(c: &mut Criterion, degree: usize, curve: &str) { - ark_commit_template::<E, DensePolynomial<E::ScalarField>>(c, black_box(degree), curve); - } - - for n in [1, 2, 4, 8, 16] { - aux::<ark_bls12_381::Bls12_381>(c, n, "BLS12-381"); - aux::<ark_bn254::Bn254>(c, n, "BN-254"); - } -} - -criterion_group!( - name = benches; - config = Criterion::default() - .warm_up_time(Duration::from_secs_f32(0.5)) - .sample_size(10); - targets = commit, ark_commit -); -criterion_main!(benches); diff --git a/benches/linalg.rs b/benches/linalg.rs deleted file mode 100644 index 547f71a11aba87f2df90fbf845f6936eeb20a4c7..0000000000000000000000000000000000000000 --- a/benches/linalg.rs +++ /dev/null @@ -1,69 +0,0 @@ -// see `benches/README.md` -use std::time::Duration; - -use ark_ff::PrimeField; - -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -use komodo::linalg::Matrix; - -fn inverse_template<F: PrimeField>(c: &mut Criterion, n: usize, curve: &str) { - let mut rng = rand::thread_rng(); - let matrix = Matrix::<F>::random(n, n, &mut rng); - - c.bench_function(&format!("inverse {}x{} on {}", n, n, curve), |b| { - b.iter(|| matrix.invert().unwrap()) - }); -} - -fn inverse(c: &mut Criterion) { - for n in [10, 15, 20, 30, 40, 60, 80, 120, 160, 240, 320] { - inverse_template::<ark_bls12_381::Fr>(c, black_box(n), "BLS12-381"); - inverse_template::<ark_bn254::Fr>(c, black_box(n), "BN-254"); - inverse_template::<ark_pallas::Fr>(c, black_box(n), "PALLAS"); - } -} - -fn transpose_template<F: PrimeField>(c: &mut Criterion, n: usize, curve: &str) { - let mut rng = rand::thread_rng(); - let matrix = Matrix::<F>::random(n, n, &mut rng); - - c.bench_function(&format!("transpose {}x{} on {}", n, n, curve), |b| { - b.iter(|| matrix.transpose()) - }); -} - -fn transpose(c: &mut Criterion) { - for n in [10, 15, 20, 30, 40, 60, 80, 120, 160, 240, 320] { - transpose_template::<ark_bls12_381::Fr>(c, black_box(n), "BLS-12-381"); - transpose_template::<ark_bn254::Fr>(c, black_box(n), "BN-254"); - transpose_template::<ark_pallas::Fr>(c, black_box(n), "PALLAS"); - } -} - -fn mul_template<F: PrimeField>(c: &mut Criterion, n: usize, curve: &str) { - let mut rng = rand::thread_rng(); - let mat_a = Matrix::<F>::random(n, n, &mut rng); - let mat_b = Matrix::<F>::random(n, n, &mut rng); - - c.bench_function(&format!("mul {}x{} on {}", n, n, curve), |b| { - b.iter(|| mat_a.mul(&mat_b)) - }); -} - -fn mul(c: &mut Criterion) { - for n in [10, 15, 20, 30, 40, 60, 80, 120] { - mul_template::<ark_bls12_381::Fr>(c, black_box(n), "BLS-12-381"); - mul_template::<ark_bn254::Fr>(c, black_box(n), "BN-254"); - mul_template::<ark_pallas::Fr>(c, black_box(n), "PALLAS"); - } -} - -criterion_group!( - name = benches; - config = Criterion::default() - .warm_up_time(Duration::from_secs_f32(0.5)) - .sample_size(10); - targets = inverse, transpose, mul -); -criterion_main!(benches); diff --git a/benches/setup.rs b/benches/setup.rs deleted file mode 100644 index 15aaa211c4b5b0083002988cc6d74a3821c00c82..0000000000000000000000000000000000000000 --- a/benches/setup.rs +++ /dev/null @@ -1,191 +0,0 @@ -// see `benches/README.md` -use std::time::Duration; - -use ark_ec::{pairing::Pairing, CurveGroup}; -use ark_ff::PrimeField; -use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; -use ark_poly_commit::kzg10::{self, KZG10}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate}; -use ark_std::ops::Div; - -use criterion::{black_box, criterion_group, criterion_main, Criterion}; - -use komodo::zk::{self, Powers}; - -fn setup_template<F, G, P>(c: &mut Criterion, degree: usize, curve: &str) -where - F: PrimeField, - G: CurveGroup<ScalarField = F>, - P: DenseUVPolynomial<F>, - for<'a, 'b> &'a P: Div<&'b P, Output = P>, -{ - let rng = &mut rand::thread_rng(); - - c.bench_function(&format!("setup (komodo) {} on {}", degree, curve), |b| { - b.iter(|| zk::setup::<F, G>(degree, rng).unwrap()) - }); -} - -fn ark_setup_template<E, P>(c: &mut Criterion, degree: usize, curve: &str) -where - E: Pairing, - P: DenseUVPolynomial<E::ScalarField>, - for<'a, 'b> &'a P: Div<&'b P, Output = P>, -{ - let rng = &mut rand::thread_rng(); - - c.bench_function( - &format!("setup (arkworks) {} bytes on {}", degree, curve), - |b| { - b.iter(|| { - let setup = KZG10::<E, P>::setup(degree, false, rng).unwrap(); - let powers_of_g = setup.powers_of_g[..=degree].to_vec(); - let powers_of_gamma_g = (0..=degree).map(|i| setup.powers_of_gamma_g[&i]).collect(); - kzg10::Powers::<E> { - powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), - powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), - } - }) - }, - ); -} - -fn serde_template<F, G, P>(c: &mut Criterion, degree: usize, curve: &str) -where - F: PrimeField, - G: CurveGroup<ScalarField = F>, - P: DenseUVPolynomial<F>, - for<'a, 'b> &'a P: Div<&'b P, Output = P>, -{ - let mut group = c.benchmark_group("setup"); - - let rng = &mut rand::thread_rng(); - - let setup = zk::setup::<F, G>(degree, rng).unwrap(); - - group.bench_function( - &format!("serializing with compression {} on {}", degree, curve), - |b| { - b.iter(|| { - let mut serialized = vec![0; setup.serialized_size(Compress::Yes)]; - setup - .serialize_with_mode(&mut serialized[..], Compress::Yes) - .unwrap(); - }) - }, - ); - - group.bench_function( - &format!("serializing with no compression {} on {}", degree, curve), - |b| { - b.iter(|| { - let mut serialized = vec![0; setup.serialized_size(Compress::No)]; - setup - .serialize_with_mode(&mut serialized[..], Compress::No) - .unwrap(); - }) - }, - ); - - for (compress, validate) in [ - (Compress::Yes, Validate::Yes), - (Compress::Yes, Validate::No), - (Compress::No, Validate::Yes), - (Compress::No, Validate::No), - ] { - let mut serialized = vec![0; setup.serialized_size(compress)]; - setup - .serialize_with_mode(&mut serialized[..], compress) - .unwrap(); - - println!( - r#"["id": "{} degree serialized with {} and {} on {}", "size": {}"#, - degree, - match compress { - Compress::Yes => "compression", - Compress::No => "no compression", - }, - match validate { - Validate::Yes => "validation", - Validate::No => "no validation", - }, - curve, - serialized.len(), - ); - - group.bench_function( - &format!( - "deserializing with {} and {} {} on {}", - match compress { - Compress::Yes => "compression", - Compress::No => "no compression", - }, - match validate { - Validate::Yes => "validation", - Validate::No => "no validation", - }, - degree, - curve - ), - |b| { - b.iter(|| { - Powers::<F, G>::deserialize_with_mode(&serialized[..], compress, validate) - }) - }, - ); - } - - group.finish(); -} - -fn setup(c: &mut Criterion) { - fn aux<F: PrimeField, G: CurveGroup<ScalarField = F>>( - c: &mut Criterion, - degree: usize, - curve: &str, - ) { - setup_template::<F, G, DensePolynomial<F>>(c, black_box(degree), curve); - } - - for n in [1, 2, 4, 8, 16] { - aux::<ark_bls12_381::Fr, ark_bls12_381::G1Projective>(c, n, "BLS-12-381"); - aux::<ark_bn254::Fr, ark_bn254::G1Projective>(c, n, "BN-254"); - aux::<ark_pallas::Fr, ark_pallas::Projective>(c, n, "PALLAS"); - } -} - -fn serde(c: &mut Criterion) { - fn aux<F: PrimeField, G: CurveGroup<ScalarField = F>>( - c: &mut Criterion, - degree: usize, - curve: &str, - ) { - serde_template::<F, G, DensePolynomial<F>>(c, black_box(degree), curve); - } - - for n in [1, 2, 4, 8, 16] { - aux::<ark_bls12_381::Fr, ark_bls12_381::G1Projective>(c, n, "BLS-12-381"); - aux::<ark_bn254::Fr, ark_bn254::G1Projective>(c, n, "BN-254"); - aux::<ark_pallas::Fr, ark_pallas::Projective>(c, n, "PALLAS"); - } -} - -fn ark_setup(c: &mut Criterion) { - fn aux<E: Pairing>(c: &mut Criterion, degree: usize, curve: &str) { - ark_setup_template::<E, DensePolynomial<E::ScalarField>>(c, black_box(degree), curve); - } - - for n in [1, 2, 4, 8, 16] { - aux::<ark_bls12_381::Bls12_381>(c, n, "BLS-12-381"); - aux::<ark_bn254::Bn254>(c, n, "BN-254"); - } -} - -criterion_group!( - name = benches; - config = Criterion::default() - .warm_up_time(Duration::from_secs_f32(0.5)) - .sample_size(10); - targets = setup, ark_setup, serde -); -criterion_main!(benches); diff --git a/examples/benches/linalg.rs b/examples/benches/linalg.rs new file mode 100644 index 0000000000000000000000000000000000000000..62fffc90655a2a438d344a12f7d231e71df488c7 --- /dev/null +++ b/examples/benches/linalg.rs @@ -0,0 +1,67 @@ +// see `benches/README.md` +use ark_ff::PrimeField; + +use clap::{arg, command, Parser}; +use komodo::linalg::Matrix; +use plnk::Bencher; + +fn inverse_template<F: PrimeField>(b: &Bencher, n: usize) { + let mut rng = rand::thread_rng(); + let matrix = Matrix::<F>::random(n, n, &mut rng); + + plnk::bench(b, &format!("inverse {}", n), || { + plnk::timeit(|| matrix.invert().unwrap()) + }); +} + +fn transpose_template<F: PrimeField>(b: &Bencher, n: usize) { + let mut rng = rand::thread_rng(); + let matrix = Matrix::<F>::random(n, n, &mut rng); + + plnk::bench(b, &format!("transpose {}", n), || { + plnk::timeit(|| matrix.transpose()) + }); +} + +fn mul_template<F: PrimeField>(b: &Bencher, n: usize) { + let mut rng = rand::thread_rng(); + let mat_a = Matrix::<F>::random(n, n, &mut rng); + let mat_b = Matrix::<F>::random(n, n, &mut rng); + + plnk::bench(b, &format!("mul {}", n), || { + plnk::timeit(|| mat_a.mul(&mat_b)) + }); +} + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Cli { + /// the sizes of the matrices to consider + #[arg(num_args = 1.., value_delimiter = ' ')] + sizes: Vec<usize>, + + /// the number of measurements to repeat each case, larger values will reduce the variance of + /// the measurements + #[arg(short, long)] + nb_measurements: usize, +} + +fn main() { + let cli = Cli::parse(); + + let b = plnk::Bencher::new(cli.nb_measurements); + + for n in cli.sizes { + inverse_template::<ark_bls12_381::Fr>(&b.with_name("BLS12-381"), n); + inverse_template::<ark_bn254::Fr>(&b.with_name("BN-254"), n); + inverse_template::<ark_pallas::Fr>(&b.with_name("PALLAS"), n); + + transpose_template::<ark_bls12_381::Fr>(&b.with_name("BLS12-381"), n); + transpose_template::<ark_bn254::Fr>(&b.with_name("BN-254"), n); + transpose_template::<ark_pallas::Fr>(&b.with_name("PALLAS"), n); + + mul_template::<ark_bls12_381::Fr>(&b.with_name("BLS12-381"), n); + mul_template::<ark_bn254::Fr>(&b.with_name("BN-254"), n); + mul_template::<ark_pallas::Fr>(&b.with_name("PALLAS"), n); + } +} diff --git a/examples/benches/setup.rs b/examples/benches/setup.rs new file mode 100644 index 0000000000000000000000000000000000000000..231466e6386886c69b7af31a75cfc68f65b4e894 --- /dev/null +++ b/examples/benches/setup.rs @@ -0,0 +1,188 @@ +// see `benches/README.md` +use ark_ec::{pairing::Pairing, CurveGroup}; +use ark_ff::PrimeField; +use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; +use ark_poly_commit::kzg10::{self, KZG10}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate}; +use ark_std::ops::Div; + +use clap::{command, Parser}; +use komodo::zk::{self, Powers}; +use plnk::Bencher; + +fn setup_template<F, G, P>(b: &Bencher, degree: usize) +where + F: PrimeField, + G: CurveGroup<ScalarField = F>, + P: DenseUVPolynomial<F>, + for<'a, 'b> &'a P: Div<&'b P, Output = P>, +{ + let rng = &mut rand::thread_rng(); + + plnk::bench(b, &format!("degree {}", degree), || { + plnk::timeit(|| zk::setup::<F, G>(degree, rng)) + }); +} + +fn ark_setup_template<E, P>(b: &Bencher, degree: usize) +where + E: Pairing, + P: DenseUVPolynomial<E::ScalarField>, + for<'a, 'b> &'a P: Div<&'b P, Output = P>, +{ + let rng = &mut rand::thread_rng(); + + plnk::bench(b, &format!("degree {}", degree), || { + plnk::timeit(|| { + let setup = KZG10::<E, P>::setup(degree, false, rng).unwrap(); + let powers_of_g = setup.powers_of_g[..=degree].to_vec(); + let powers_of_gamma_g = (0..=degree).map(|i| setup.powers_of_gamma_g[&i]).collect(); + kzg10::Powers::<E> { + powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), + powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), + } + }) + }); +} + +#[allow(dead_code)] +fn serde_template<F, G, P>(b: &Bencher, degree: usize) +where + F: PrimeField, + G: CurveGroup<ScalarField = F>, + P: DenseUVPolynomial<F>, + for<'a, 'b> &'a P: Div<&'b P, Output = P>, +{ + let rng = &mut rand::thread_rng(); + + let setup = zk::setup::<F, G>(degree, rng).unwrap(); + + plnk::bench( + b, + &format!("serializing with compression {}", degree), + || { + plnk::timeit(|| { + let mut serialized = vec![0; setup.serialized_size(Compress::Yes)]; + setup + .serialize_with_mode(&mut serialized[..], Compress::Yes) + .unwrap(); + }) + }, + ); + + plnk::bench( + b, + &format!("serializing with no compression {}", degree), + || { + plnk::timeit(|| { + let mut serialized = vec![0; setup.serialized_size(Compress::No)]; + setup + .serialize_with_mode(&mut serialized[..], Compress::No) + .unwrap(); + }) + }, + ); + + for (compress, validate) in [ + (Compress::Yes, Validate::Yes), + (Compress::Yes, Validate::No), + (Compress::No, Validate::Yes), + (Compress::No, Validate::No), + ] { + let mut serialized = vec![0; setup.serialized_size(compress)]; + setup + .serialize_with_mode(&mut serialized[..], compress) + .unwrap(); + + plnk::bench( + b, + &format!( + "deserializing with {} and {} {}", + match compress { + Compress::Yes => "compression", + Compress::No => "no compression", + }, + match validate { + Validate::Yes => "validation", + Validate::No => "no validation", + }, + degree, + ), + || { + plnk::timeit(|| { + Powers::<F, G>::deserialize_with_mode(&serialized[..], compress, validate) + }) + }, + ); + } +} + +fn setup(degrees: &[usize], nb_measurements: usize) { + fn aux<F: PrimeField, G: CurveGroup<ScalarField = F>>( + degree: usize, + curve: &str, + nb_measurements: usize, + ) { + let b = plnk::Bencher::new(nb_measurements).with_name(format!("setup on {}", curve)); + setup_template::<F, G, DensePolynomial<F>>(&b, degree); + } + + for d in degrees { + aux::<ark_bls12_381::Fr, ark_bls12_381::G1Projective>(*d, "BLS-12-381", nb_measurements); + aux::<ark_bn254::Fr, ark_bn254::G1Projective>(*d, "BN-254", nb_measurements); + aux::<ark_pallas::Fr, ark_pallas::Projective>(*d, "PALLAS", nb_measurements); + } +} + +#[allow(dead_code)] +fn serde(degrees: &[usize], nb_measurements: usize) { + fn aux<F: PrimeField, G: CurveGroup<ScalarField = F>>( + degree: usize, + curve: &str, + nb_measurements: usize, + ) { + let b = + plnk::Bencher::new(nb_measurements).with_name(format!("serialization on {}", curve)); + serde_template::<F, G, DensePolynomial<F>>(&b, degree); + } + + for d in degrees { + aux::<ark_bls12_381::Fr, ark_bls12_381::G1Projective>(*d, "BLS-12-381", nb_measurements); + aux::<ark_bn254::Fr, ark_bn254::G1Projective>(*d, "BN-254", nb_measurements); + aux::<ark_pallas::Fr, ark_pallas::Projective>(*d, "PALLAS", nb_measurements); + } +} + +fn ark_setup(degrees: &[usize], nb_measurements: usize) { + fn aux<E: Pairing>(degree: usize, curve: &str, nb_measurements: usize) { + let b = plnk::Bencher::new(nb_measurements).with_name(format!("ARK setup on {}", curve)); + ark_setup_template::<E, DensePolynomial<E::ScalarField>>(&b, degree); + } + + for d in degrees { + aux::<ark_bls12_381::Bls12_381>(*d, "BLS-12-381", nb_measurements); + aux::<ark_bn254::Bn254>(*d, "BN-254", nb_measurements); + } +} + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Cli { + /// the polynomial degrees to measure the commit time on + #[arg(num_args = 1.., value_delimiter = ' ')] + degrees: Vec<usize>, + + /// the number of measurements to repeat each case, larger values will reduce the variance of + /// the measurements + #[arg(short, long)] + nb_measurements: usize, +} + +fn main() { + let cli = Cli::parse(); + + setup(&cli.degrees, cli.nb_measurements); + ark_setup(&cli.degrees, cli.nb_measurements); + // NOTE: this is disabled for now because it takes so much time... + // serde(&cli.degrees, cli.nb_measurements); +} diff --git a/scripts/parse.nu b/scripts/parse.nu new file mode 100644 index 0000000000000000000000000000000000000000..3fb4b86990fe33ba3e0065c2f0acb79190584365 --- /dev/null +++ b/scripts/parse.nu @@ -0,0 +1,22 @@ +export def read-atomic-ops [ + --include: list<string> = [], --exclude: list<string> = [] +]: list -> record { + let raw = $in + | insert t {|it| $it.times |math avg} + | reject times + | rename --column { label: "group", name: "species", t: "measurement" } + + let included = if $include != [] { + $raw | where group in $include + } else { + $raw + } + + $included + | where group not-in $exclude + | group-by group --to-table + | reject items.group + | update items { transpose -r | into record } + | transpose -r + | into record +} diff --git a/scripts/plot/bench_commit.py b/scripts/plot/bench_commit.py deleted file mode 100644 index ba9f5033b29251f8e7ed5c2cd1798a59d95527b2..0000000000000000000000000000000000000000 --- a/scripts/plot/bench_commit.py +++ /dev/null @@ -1,31 +0,0 @@ -# see `benches/README.md` -import json -import sys -import matplotlib.pyplot as plt - -NB_NS_IN_MS = 1e6 - - -if __name__ == "__main__": - data = json.loads(sys.argv[1]) - - for group in data: - xs = [x["degree"] for x in group["items"]] - ys = [x["mean"] / NB_NS_IN_MS for x in group["items"]] - zs = [x["stddev"] / NB_NS_IN_MS for x in group["items"]] - - down = [y - z for (y, z) in zip(ys, zs)] - up = [y + z for (y, z) in zip(ys, zs)] - - style = "dashed" if group["group"].endswith("-ark") else "solid" - plt.plot(xs, ys, label=group["group"], marker='o', linestyle=style) - plt.fill_between(xs, down, up, alpha=0.3) - - plt.xlabel("degree") - plt.ylabel("time (in ms)") - - plt.title("time to commit polynomials for certain curves") - - plt.legend() - plt.grid(True) - plt.show() diff --git a/scripts/plot/benches.py b/scripts/plot/benches.py deleted file mode 100644 index a525783f39cc87474e721e36869a56f22c7f3085..0000000000000000000000000000000000000000 --- a/scripts/plot/benches.py +++ /dev/null @@ -1,242 +0,0 @@ -# see `benches/README.md` -import matplotlib.pyplot as plt -import json -import sys -import os -import argparse -from typing import Any, Dict, List - -NB_NS_IN_MS = 1e6 -NB_BYTES_IN_KB = 1_024 - -FULLSCREEN_DPI = 300 - -# represents a full NDJSON dataset, i.e. directly generated by `cargo criterion`, -# filtered to remove invalid lines, e.g. whose `$.reason` is not -# `benchmark-complete` -Data = List[Dict[str, Any]] - - -# k1: namely `mean` or `median` -# k2: namely `estimation`, `upper_bound`, `lower_bound` or None -def extract(data: Data, k1: str, k2: str) -> List[float]: - return [line[k1][k2] if k2 is not None else line[k1] for line in data] - - -# convert a list of times in nanoseconds to the same list in milliseconds -def ns_to_ms(times: List[float]) -> List[float]: - return [t / NB_NS_IN_MS for t in times] - - -# convert a list of sizes in bytes to the same list in kilobytes -def b_to_kb(sizes: List[int]) -> List[float]: - return [s / NB_BYTES_IN_KB for s in sizes] - - -# read a result dataset from an NDJSON file and filter out invalid lines -# -# here, invalid lines are all the lines with `$.reason` not equal to -# `benchmark-complete` that are generated by `cargo criterion` but useless. -def read_data(data_file: str) -> Data: - if not os.path.exists(data_file): - print(f"no such file: `{data_file}`") - exit(1) - - with open(data_file, "r") as file: - data = list(filter( - lambda line: line["reason"] == "benchmark-complete", - map( - json.loads, - file.readlines() - ) - )) - - return data - - -def plot_linalg(data: Data, save: bool = False): - # key: the start of the `$.id` field - def plot(data: Data, key: str, curve: str, color: str, ax): - filtered_data = list(filter( - lambda line: line["id"].startswith(key) and line["id"].endswith(f" on {curve}"), - data - )) - if len(filtered_data) == 0: - return - - sizes = [ - int(line["id"].split(' ')[1].split('x')[0]) for line in filtered_data - ] - - means = ns_to_ms(extract(filtered_data, "mean", "estimate")) - up = ns_to_ms(extract(filtered_data, "mean", "upper_bound")) - down = ns_to_ms(extract(filtered_data, "mean", "lower_bound")) - - ax.plot(sizes, means, label=curve, color=color) - ax.fill_between(sizes, down, up, color=color, alpha=0.3) - - keys = ["transpose", "mul", "inverse"] - - fig, axs = plt.subplots(len(keys), 1, figsize=(16, 9)) - - for key, ax in zip(keys, axs): - for (curve, color) in [("BLS12-381", "blue"), ("BN-254", "orange"), ("PALLAS", "green")]: - plot(data, key=key, curve=curve, color=color, ax=ax) - ax.set_title(key) - ax.set_yscale("log") - ax.set_ylabel("time in ms") - ax.legend() - ax.grid() - - if save: - output = "linalg.png" - plt.savefig(output, dpi=FULLSCREEN_DPI) - print(f"figure saved as {output}") - else: - plt.show() - - -def plot_setup(data: Data, save: bool = False): - fig, axs = plt.subplots(4, 1, sharex=True, figsize=(16, 9)) - - # key: the start of the `$.id` field - def plot(data: Data, key: str, curve: str, label: str, color: str, style: str, error_bar: bool, ax): - filtered_data = list(filter( - lambda line: line["id"].startswith(key) and line["id"].endswith(f" on {curve}"), - data - )) - if len(filtered_data) == 0: - return - - sizes = [int(line["id"].lstrip(key).split(' ')[0]) for line in filtered_data] - - if error_bar: - means = ns_to_ms(extract(filtered_data, "mean", "estimate")) - up = ns_to_ms(extract(filtered_data, "mean", "upper_bound")) - down = ns_to_ms(extract(filtered_data, "mean", "lower_bound")) - else: - means = b_to_kb(extract(filtered_data, "mean", None)) - - ax.plot(sizes, means, label=f"{label} on {curve}", color=color, linestyle=style) - - if error_bar: - ax.fill_between(sizes, down, up, color=color, alpha=0.3) - - # setup - for (curve, color) in [("BLS12-381", "blue"), ("BN-254", "orange"), ("PALLAS", "green")]: - plot(data, "setup/setup (komodo)", curve, "komodo", color, "solid", True, axs[0]) - plot(data, "setup (arkworks)", curve, "arkworks", color, "dashed", True, axs[0]) - axs[0].set_title("time to generate a random trusted setup") - axs[0].set_ylabel("time (in ms)") - axs[0].legend() - axs[0].grid() - - # serialization - for (curve, color) in [("BLS12-381", "blue"), ("BN-254", "orange"), ("PALLAS", "green")]: - plot(data, "setup/serializing with compression", curve, "compressed", color, "solid", True, axs[1]) - plot(data, "setup/serializing with no compression", curve, "uncompressed", color, "dashed", True, axs[1]) - axs[1].set_title("serialization") - axs[1].set_ylabel("time (in ms)") - axs[1].legend() - axs[1].grid() - - # deserialization - for (curve, color) in [("BLS12-381", "blue"), ("BN-254", "orange"), ("PALLAS", "green")]: - plot(data, "setup/deserializing with no compression and no validation", curve, "uncompressed unvalidated", color, "dotted", True, axs[2]) - plot(data, "setup/deserializing with compression and no validation", curve, "compressed unvalidated", color, "dashed", True, axs[2]) - plot(data, "setup/deserializing with no compression and validation", curve, "uncompressed validated", color, "dashdot", True, axs[2]) - plot(data, "setup/deserializing with compression and validation", curve, "compressed validated", color, "solid", True, axs[2]) - axs[2].set_title("deserialization") - axs[2].set_ylabel("time (in ms)") - axs[2].legend() - axs[2].grid() - - for (curve, color) in [("BLS12-381", "blue"), ("BN-254", "orange"), ("PALLAS", "green")]: - plot(data, "serialized size with no compression", curve, "uncompressed", color, "dashed", False, axs[3]) - plot(data, "serialized size with compression", curve, "compressed", color, "solid", False, axs[3]) - axs[3].set_title("size") - axs[3].set_xlabel("degree") - axs[3].set_ylabel("size (in kb)") - axs[3].legend() - axs[3].grid() - - if save: - output = "setup.png" - plt.savefig(output, dpi=FULLSCREEN_DPI) - print(f"figure saved as {output}") - else: - plt.show() - - -def plot_commit(data: Data, save: bool = False): - fig, ax = plt.subplots(1, 1, figsize=(16, 9)) - - # key: the start of the `$.id` field - def plot(data: Data, key: str, curve: str, style: str, color: str, ax): - filtered_data = list(filter( - lambda line: line["id"].startswith(key) and line["id"].endswith(f" on {curve}"), - data - )) - if len(filtered_data) == 0: - return - - sizes = [ - int(line["id"].lstrip(key).split(' ')[0]) for line in filtered_data - ] - - means = ns_to_ms(extract(filtered_data, "mean", "estimate")) - up = ns_to_ms(extract(filtered_data, "mean", "upper_bound")) - down = ns_to_ms(extract(filtered_data, "mean", "lower_bound")) - - ax.plot(sizes, means, label=f"{key} on {curve}", color=color, linestyle=style) - ax.fill_between(sizes, down, up, color=color, linestyle=style, alpha=0.3) - - for (curve, color) in [("BLS12-381", "blue"), ("BN-254", "orange"), ("PALLAS", "green")]: - plot(data, key="commit (komodo)", curve=curve, style="solid", color=color, ax=ax) - plot(data, key="commit (arkworks)", curve=curve, style="dashed", color=color, ax=ax) - - ax.set_title("commit times") - ax.set_ylabel("time (in ms)") - ax.set_xlabel("degree") - ax.legend() - ax.grid(True) - - if save: - output = "commit.png" - plt.savefig(output, dpi=FULLSCREEN_DPI) - print(f"figure saved as {output}") - else: - plt.show() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("filename", type=str) - parser.add_argument( - "--bench", "-b", type=str, choices=["linalg", "setup", "commit"], - ) - parser.add_argument( - "--save", "-s", action="store_true", default=False, - ) - parser.add_argument( - "--all", "-a", action="store_true", default=False, - ) - args = parser.parse_args() - - data = read_data(args.filename) - - if args.all: - plot_linalg(data, save=args.save) - plot_setup(data, save=args.save) - plot_commit(data, save=args.save) - exit(0) - - match args.bench: - case "linalg": - plot_linalg(data, save=args.save) - case "setup": - plot_setup(data, save=args.save) - case "commit": - plot_commit(data, save=args.save) - case _: - print("nothing to do: you might want to use `--bench <bench>` or `--all`") diff --git a/scripts/plot/plot.py b/scripts/plot/plot.py new file mode 100644 index 0000000000000000000000000000000000000000..339fd9092ea16f1fc5b0a8c89f21b856f1d01fbd --- /dev/null +++ b/scripts/plot/plot.py @@ -0,0 +1,104 @@ +# see `benches/README.md` +import json +import sys +import matplotlib.pyplot as plt +import argparse + + +# # Example +# ```nuon +# [ +# { +# group: "Alice", +# items: [ +# [ x, measurement, error ]; +# [ 1, 1143, 120 ], +# [ 2, 1310, 248 ], +# [ 4, 1609, 258 ], +# [ 8, 1953, 343 ], +# [ 16, 2145, 270 ], +# [ 32, 3427, 301 ] +# ] +# }, +# { +# group: "Bob", +# items: [ +# [ x, measurement, error ]; +# [ 1, 2388, 374 ], +# [ 2, 2738, 355 ], +# [ 4, 3191, 470 ], +# [ 8, 3932, 671 ], +# [ 16, 4571, 334 ], +# [ 32, 4929, 1094 ] +# ] +# }, +# ] +# ``` +def plot(data, title: str, x_label: str, y_label: str, save: str = None): + for group in data: + xs = [x["x"] for x in group["items"]] + ys = [x["measurement"] for x in group["items"]] + zs = [x["error"] for x in group["items"]] + + down = [y - z for (y, z) in zip(ys, zs)] + up = [y + z for (y, z) in zip(ys, zs)] + + style = "dashed" if group["group"].endswith("-ark") else "solid" + plt.plot(xs, ys, label=group["group"], marker='o', linestyle=style) + plt.fill_between(xs, down, up, alpha=0.3) + + plt.xlabel(x_label) + plt.ylabel(y_label) + + plt.title(title) + + plt.legend() + plt.grid(True) + + 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: + [ + { + group: "Alice", + items: [ + [ x, measurement, error ]; + [ 1, 1143, 120 ], + [ 2, 1310, 248 ], + [ 4, 1609, 258 ], + [ 8, 1953, 343 ], + [ 16, 2145, 270 ], + [ 32, 3427, 301 ] + ] + }, + { + group: "Bob", + items: [ + [ x, measurement, error ]; + [ 1, 2388, 374 ], + [ 2, 2738, 355 ], + [ 4, 3191, 470 ], + [ 8, 3932, 671 ], + [ 16, 4571, 334 ], + [ 32, 4929, 1094 ] + ] + }, + ] + """) + parser.add_argument("--title", "-t", type=str, help="the title of the plot") + parser.add_argument("--x-label", "-x", type=str, help="the x label of the plot") + parser.add_argument("--y-label", "-y", type=str, help="the y label of the plot") + parser.add_argument("--save", "-s", type=str, help="a path to save the figure to") + args = parser.parse_args() + + plot(json.loads(args.data), args.title, args.x_label, args.y_label, save=args.save)