diff --git a/Cargo.toml b/Cargo.toml
index 09da5682a5e1ad04928a78c1ef03723d6fddedd4..26178092bae5fea858235f64d7194a843aae2e74 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,6 +14,7 @@ ark-poly = "0.4.2"
 ark-serialize = "0.4.2"
 ark-std = "0.4.0"
 clap = { version = "4.5.4", features = ["derive"] }
+plnk = { git = "https://gitlab.isae-supaero.fr/a.stevan/plnk", tag = "0.3.0", version = "0.3.0" }
 rand = "0.8.5"
 rs_merkle = "1.4.1"
 thiserror = "1.0.50"
diff --git a/benches/README.md b/benches/README.md
index cf3482aa84c2f6c0a1da92783dfbefb0ca8f8f2b..2c1f21f1c68c8926cee5de1cb2887e5772f6fd6d 100644
--- a/benches/README.md
+++ b/benches/README.md
@@ -18,12 +18,12 @@ python scripts/plot/benches.py results.ndjson --bench setup
 ```nushell
 cargo run --example bench_field_operations -- --nb-measurements 1000
     | lines
-    | each { from nuon }
+    | each { from json }
     | to ndjson
     | save --force field.ndjson
 cargo run --example bench_curve_group_operations -- --nb-measurements 1000
     | lines
-    | each { from nuon }
+    | each { from json }
     | to ndjson
     | save --force curve_group.ndjson
 ```
@@ -34,7 +34,7 @@ def read-atomic-ops [
     let raw = $in
         | insert t {|it| $it.times |math avg}
         | reject times
-        | rename --column { op: "group", curve: "species", t: "measurement" }
+        | rename --column { label: "group", name: "species", t: "measurement" }
 
     let included = if $include != [] {
         $raw | where group in $include
diff --git a/examples/benches/operations/curve_group.rs b/examples/benches/operations/curve_group.rs
index d6d2771a9e420c61067e04909ff39be276d75e14..978c7e00fc2eb5b3042357a335caa5760dcec4ff 100644
--- a/examples/benches/operations/curve_group.rs
+++ b/examples/benches/operations/curve_group.rs
@@ -1,118 +1,64 @@
 // see `benches/README.md`
-use std::time::{Duration, Instant};
+use std::time::Instant;
 
 use ark_ec::CurveGroup;
 use ark_ff::PrimeField;
 use clap::{command, Parser};
-use rand::RngCore;
-
-fn bench(b: &Bencher, op: &str, thing: fn(&mut dyn RngCore) -> Duration) {
-    let mut rng = ark_std::test_rng();
-
-    let mut times = vec![];
-    for i in 0..b.nb_measurements {
-        eprint!(
-            "{} on {} [{:>5}/{}]\r",
-            op,
-            b.name,
-            i + 1,
-            b.nb_measurements
-        );
-
-        times.push(thing(&mut rng).as_nanos());
-    }
-    eprintln!();
-
-    println!(
-        r#"{{op: "{}", curve: "{}", times: {:?}}}"#,
-        op, b.name, times
-    );
-}
-
-macro_rules! timeit {
-    ($f:tt) => {{
-        let start_time = Instant::now();
-        #[allow(clippy::redundant_closure_call)]
-        let _ = $f();
-        Instant::now().duration_since(start_time)
-    }};
-}
-
-#[derive(Clone)]
-struct Bencher {
-    nb_measurements: usize,
-    name: String,
-}
-
-impl Bencher {
-    fn new(nb_measurements: usize) -> Self {
-        Self {
-            nb_measurements,
-            name: "".to_string(),
-        }
-    }
-
-    fn with_name(&self, name: impl ToString) -> Self {
-        let mut new = self.clone();
-        new.name = name.to_string();
-        new
-    }
-}
 
-fn bench_template<F: PrimeField, G: CurveGroup<ScalarField = F>>(b: &Bencher) {
-    bench(b, "random sampling", |rng| timeit!((|| G::rand(rng))));
+fn bench_template<F: PrimeField, G: CurveGroup<ScalarField = F>>(b: &mut plnk::Bencher) {
+    plnk::bench(b, "random sampling", |rng| plnk::timeit!((|| G::rand(rng))));
 
-    bench(b, "addition", |rng| {
+    plnk::bench(b, "addition", |rng| {
         let g1 = G::rand(rng);
         let g2 = G::rand(rng);
 
-        timeit!((|| g1 + g2))
+        plnk::timeit!((|| g1 + g2))
     });
 
-    bench(b, "substraction", |rng| {
+    plnk::bench(b, "substraction", |rng| {
         let g1 = G::rand(rng);
         let g2 = G::rand(rng);
 
-        timeit!((|| g1 - g2))
+        plnk::timeit!((|| g1 - g2))
     });
 
-    bench(b, "double", |rng| {
+    plnk::bench(b, "double", |rng| {
         let g1 = G::rand(rng);
 
-        timeit!((|| g1.double()))
+        plnk::timeit!((|| g1.double()))
     });
 
-    bench(b, "scalar multiplication", |rng| {
+    plnk::bench(b, "scalar multiplication", |rng| {
         let g1 = G::rand(rng);
         let f1 = F::rand(rng);
 
-        timeit!((|| g1.mul(f1)))
+        plnk::timeit!((|| g1.mul(f1)))
     });
 
-    bench(b, "into affine", |rng| {
+    plnk::bench(b, "into affine", |rng| {
         let g1 = G::rand(rng);
 
-        timeit!((|| g1.into_affine()))
+        plnk::timeit!((|| g1.into_affine()))
     });
 
-    bench(b, "from affine", |rng| {
+    plnk::bench(b, "from affine", |rng| {
         let g1_affine = G::rand(rng).into_affine();
 
-        timeit!((|| Into::<G>::into(g1_affine)))
+        plnk::timeit!((|| Into::<G>::into(g1_affine)))
     });
 
-    bench(b, "affine addition", |rng| {
+    plnk::bench(b, "affine addition", |rng| {
         let g1_affine = G::rand(rng).into_affine();
         let g2_affine = G::rand(rng).into_affine();
 
-        timeit!((|| g1_affine + g2_affine))
+        plnk::timeit!((|| g1_affine + g2_affine))
     });
 
-    bench(b, "affine scalar multiplication", |rng| {
+    plnk::bench(b, "affine scalar multiplication", |rng| {
         let g1_affine = G::rand(rng).into_affine();
         let f1 = F::rand(rng);
 
-        timeit!((|| g1_affine * f1))
+        plnk::timeit!((|| g1_affine * f1))
     });
 }
 
@@ -128,11 +74,11 @@ struct Cli {
 fn main() {
     let cli = Cli::parse();
 
-    let bencher = Bencher::new(cli.nb_measurements);
+    let bencher = plnk::Bencher::new(cli.nb_measurements, ark_std::rand::thread_rng());
 
     bench_template::<ark_bls12_381::Fr, ark_bls12_381::G1Projective>(
-        &bencher.with_name("BLS12-381"),
+        &mut bencher.with_name("BLS12-381"),
     );
-    bench_template::<ark_bn254::Fr, ark_bn254::G1Projective>(&bencher.with_name("BN-254"));
-    bench_template::<ark_pallas::Fr, ark_pallas::Projective>(&bencher.with_name("PALLAS"));
+    bench_template::<ark_bn254::Fr, ark_bn254::G1Projective>(&mut bencher.with_name("BN-254"));
+    bench_template::<ark_pallas::Fr, ark_pallas::Projective>(&mut bencher.with_name("PALLAS"));
 }
diff --git a/examples/benches/operations/field.rs b/examples/benches/operations/field.rs
index 5d4a8b6a1c12683edbc64dc00222080d626b3c12..b37b1ec75b7175587128dd7d13783e49834a00f7 100644
--- a/examples/benches/operations/field.rs
+++ b/examples/benches/operations/field.rs
@@ -3,128 +3,74 @@ use std::time::{Duration, Instant};
 
 use ark_ff::PrimeField;
 use clap::{arg, command, Parser};
-use rand::RngCore;
-
-fn bench(b: &Bencher, op: &str, thing: fn(&mut dyn RngCore) -> Duration) {
-    let mut rng = ark_std::test_rng();
-
-    let mut times = vec![];
-    for i in 0..b.nb_measurements {
-        eprint!(
-            "{} on {} [{:>5}/{}]\r",
-            op,
-            b.name,
-            i + 1,
-            b.nb_measurements
-        );
-
-        times.push(thing(&mut rng).as_nanos());
-    }
-    eprintln!();
-
-    println!(
-        r#"{{op: "{}", curve: "{}", times: {:?}}}"#,
-        op, b.name, times
-    );
-}
-
-macro_rules! timeit {
-    ($f:tt) => {{
-        let start_time = Instant::now();
-        #[allow(clippy::redundant_closure_call)]
-        let _ = $f();
-        Instant::now().duration_since(start_time)
-    }};
-}
-
-#[derive(Clone)]
-struct Bencher {
-    nb_measurements: usize,
-    name: String,
-}
-
-impl Bencher {
-    fn new(nb_measurements: usize) -> Self {
-        Self {
-            nb_measurements,
-            name: "".to_string(),
-        }
-    }
-
-    fn with_name(&self, name: impl ToString) -> Self {
-        let mut new = self.clone();
-        new.name = name.to_string();
-        new
-    }
-}
 
-fn bench_template<F: PrimeField>(b: &Bencher) {
-    bench(b, "random sampling", |rng| timeit!((|| F::rand(rng))));
+fn bench_template<F: PrimeField>(b: &mut plnk::Bencher) {
+    plnk::bench(b, "random sampling", |rng| plnk::timeit!((|| F::rand(rng))));
 
-    bench(b, "addition", |rng| {
+    plnk::bench(b, "addition", |rng| {
         let f1 = F::rand(rng);
         let f2 = F::rand(rng);
 
-        timeit!((|| f1 + f2))
+        plnk::timeit!((|| f1 + f2))
     });
 
-    bench(b, "substraction", |rng| {
+    plnk::bench(b, "substraction", |rng| {
         let f1 = F::rand(rng);
         let f2 = F::rand(rng);
 
-        timeit!((|| f1 - f2))
+        plnk::timeit!((|| f1 - f2))
     });
 
-    bench(b, "double", |rng| {
+    plnk::bench(b, "double", |rng| {
         let f1 = F::rand(rng);
 
-        timeit!((|| f1.double()))
+        plnk::timeit!((|| f1.double()))
     });
 
-    bench(b, "multiplication", |rng| {
+    plnk::bench(b, "multiplication", |rng| {
         let f1 = F::rand(rng);
         let f2 = F::rand(rng);
 
-        timeit!((|| f1 * f2))
+        plnk::timeit!((|| f1 * f2))
     });
 
-    bench(b, "square", |rng| {
+    plnk::bench(b, "square", |rng| {
         let f1 = F::rand(rng);
 
-        timeit!((|| f1.square()))
+        plnk::timeit!((|| f1.square()))
     });
 
-    bench(b, "inverse", |rng| {
+    plnk::bench(b, "inverse", |rng| {
         let f1 = F::rand(rng);
 
-        timeit!((|| f1.inverse()))
+        plnk::timeit!((|| f1.inverse()))
     });
 
-    bench(b, "legendre", |rng| {
+    plnk::bench(b, "legendre", |rng| {
         let f1 = F::rand(rng);
 
-        timeit!((|| f1.legendre()))
+        plnk::timeit!((|| f1.legendre()))
     });
 
-    bench(b, "sqrt", |rng| {
+    plnk::bench(b, "sqrt", |rng| {
         let f1 = F::rand(rng);
         if f1.legendre().is_qr() {
-            timeit!((|| f1.sqrt()))
+            plnk::timeit!((|| f1.sqrt()))
         } else {
             Duration::default()
         }
     });
 
-    bench(b, "exponentiation", |rng| {
+    plnk::bench(b, "exponentiation", |rng| {
         let f1 = F::rand(rng);
 
-        timeit!((|| f1.pow(F::MODULUS)))
+        plnk::timeit!((|| f1.pow(F::MODULUS)))
     });
 
-    bench(b, "into bigint", |rng| {
+    plnk::bench(b, "into bigint", |rng| {
         let f1 = F::rand(rng);
 
-        timeit!((|| f1.into_bigint()))
+        plnk::timeit!((|| f1.into_bigint()))
     });
 }
 
@@ -140,9 +86,9 @@ struct Cli {
 fn main() {
     let cli = Cli::parse();
 
-    let bencher = Bencher::new(cli.nb_measurements);
+    let bencher = plnk::Bencher::new(cli.nb_measurements, ark_std::rand::thread_rng());
 
-    bench_template::<ark_bls12_381::Fr>(&bencher.with_name("BLS12-381"));
-    bench_template::<ark_bn254::Fr>(&bencher.with_name("BN-254"));
-    bench_template::<ark_pallas::Fr>(&bencher.with_name("PALLAS"));
+    bench_template::<ark_bls12_381::Fr>(&mut bencher.with_name("BLS12-381"));
+    bench_template::<ark_bn254::Fr>(&mut bencher.with_name("BN-254"));
+    bench_template::<ark_pallas::Fr>(&mut bencher.with_name("PALLAS"));
 }