diff --git a/Cargo.toml b/Cargo.toml index 6e71ae55b967076c58a5ef9da9020af57fe4a4d0..da9d6d2851e10ced1581a9d3b9775d99efc559fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,3 +78,7 @@ path = "examples/benches/linalg.rs" [[example]] name = "bench_recoding" path = "examples/benches/recoding.rs" + +[[example]] +name = "bench_fec" +path = "examples/benches/fec.rs" diff --git a/examples/benches/README.md b/examples/benches/README.md index a01ef2a35dd050bf79f445d071218ee69d8d275a..6939c07b77f068ec1f85ad6cf2f76524f4701e29 100644 --- a/examples/benches/README.md +++ b/examples/benches/README.md @@ -166,3 +166,73 @@ python scripts/plot/plot.py --title "recoding with k = 4" ( | to json ) ``` + +### FEC +```nushell +let rho = 1 / 2 + +"" out> fec.ndjson + +[3, 5] | each { |k| + cargo run --example bench_fec -- ...[ + ...[100, 1_000, 10_000] + --encoding vandermonde + -k $k + -n ($k / $rho) + --nb-measurements 100 + ] | from ndnuon | to ndjson out>> fec.ndjson +} +``` +```nushell +python scripts/plot/plot.py --title "encoding" --x-label "nb bytes" --y-label "time (in ms)" ( + open fec.ndjson + | update label { from json } + | flatten label + | ns-to-ms times + | compute-stats times + | insert foo { $"($in.name) / ($in.k)" } + | where step == "encode" + | rename --column { bytes: "x", mean: "y", stddev: "e" } + | group-by foo --to-table + | rename --column { group: "name", items: "points" } + | to json +) + +python scripts/plot/plot.py --title "decoding" --x-label "nb bytes" --y-label "time (in ms)" ( + open fec.ndjson + | update label { from json } + | flatten label + | ns-to-ms times + | compute-stats times + | insert foo { $"($in.name) / ($in.k)" } + | where step == "decode" + | rename --column { bytes: "x", mean: "y", stddev: "e" } + | group-by foo --to-table + | rename --column { group: "name", items: "points" } + | to json +) + +let x = open fec.ndjson + | update label { from json } + | flatten label + | insert foo { $"($in.name) / ($in.k) / ($in.bytes)" } + | group-by foo --to-table + | update items {|it| + $it.items + | update step e2e + | update times { $it.items.0.times | zip $it.items.1.times | each { $in.0 + $in.1 } } + } + | flatten --all + | reject group foo + +python scripts/plot/plot.py --title "e2e" --x-label "nb bytes" --y-label "time (in ms)" ( + $x + | ns-to-ms times + | compute-stats times + | insert foo { $"($in.name) / ($in.k)" } + | rename --column { bytes: "x", mean: "y", stddev: "e" } + | group-by foo --to-table + | rename --column { group: "name", items: "points" } + | to json +) +``` diff --git a/examples/benches/fec.rs b/examples/benches/fec.rs new file mode 100644 index 0000000000000000000000000000000000000000..65463e4ff2e60c4e5bf06808ce3653893ee5a8c4 --- /dev/null +++ b/examples/benches/fec.rs @@ -0,0 +1,100 @@ +// see `examples/benches/README.md` +use ark_ff::PrimeField; + +use clap::{arg, command, Parser, ValueEnum}; +use komodo::{fec, linalg::Matrix}; +use plnk::Bencher; +use rand::{rngs::ThreadRng, thread_rng, Rng, RngCore}; + +fn random_bytes(n: usize, rng: &mut ThreadRng) -> Vec<u8> { + (0..n).map(|_| rng.gen::<u8>()).collect() +} + +fn build_encoding_mat<F: PrimeField>( + k: usize, + n: usize, + encoding: &Encoding, + rng: &mut impl RngCore, +) -> Matrix<F> { + match encoding { + Encoding::Random => Matrix::random(k, n, rng), + Encoding::Vandermonde => { + let points: Vec<F> = (0..n) + .map(|i| F::from_le_bytes_mod_order(&i.to_le_bytes())) + .collect(); + Matrix::vandermonde_unchecked(&points, k) + } + } +} + +fn template<F: PrimeField>(b: &Bencher, nb_bytes: usize, k: usize, n: usize, encoding: &Encoding) { + let mut rng = thread_rng(); + + let encoding_mat = build_encoding_mat(k, n, encoding, &mut rng); + + plnk::bench( + b, + &format!( + r#"{{"bytes": {}, "step": "encode", "k": {}, "n": {}}}"#, + nb_bytes, k, n + ), + || { + let bytes = random_bytes(nb_bytes, &mut rng); + + plnk::timeit(|| fec::encode::<F>(&bytes, &encoding_mat).unwrap()) + }, + ); + + plnk::bench( + b, + &format!( + r#"{{"bytes": {}, "step": "decode", "k": {}, "n": {}}}"#, + nb_bytes, k, n + ), + || { + let bytes = random_bytes(nb_bytes, &mut rng); + let shards = fec::encode::<F>(&bytes, &encoding_mat).unwrap(); + + plnk::timeit(|| fec::decode::<F>(shards.clone()).unwrap()) + }, + ); +} + +#[derive(ValueEnum, Clone)] +enum Encoding { + Vandermonde, + Random, +} + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Cli { + /// the sizes of the data to consider + #[arg(num_args = 1.., value_delimiter = ' ')] + sizes: Vec<usize>, + + #[arg(short, long)] + encoding: Encoding, + + #[arg(short)] + k: usize, + #[arg(short)] + n: usize, + + /// the number of measurements to repeat each case, larger values will reduce the variance of + /// the measurements + #[arg(long)] + nb_measurements: usize, +} + +fn main() { + let cli = Cli::parse(); + + let b = plnk::Bencher::new(cli.nb_measurements); + + for n in cli.sizes { + template::<ark_bls12_381::Fr>(&b.with_name("BLS12-381"), n, cli.k, cli.n, &cli.encoding); + template::<ark_bn254::Fr>(&b.with_name("BN-254"), n, cli.k, cli.n, &cli.encoding); + template::<ark_pallas::Fr>(&b.with_name("PALLAS"), n, cli.k, cli.n, &cli.encoding); + } +}