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);
+    }
+}