use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use p3_binary_field::{BinaryChallenger, BinaryField128, Gf2, PackedGf2x64};
use p3_binary_pcs::{
BinaryPcs, BinaryPcsConfig, BinaryPcsParams, BooleanMultilinearPcs, BooleanPcs,
};
use p3_challenger::HashChallenger;
use p3_commit::MultilinearPcs;
use p3_field::PrimeCharacteristicRing;
use p3_keccak::Keccak256Hash;
use p3_matrix::dense::RowMajorMatrix;
use p3_merkle_tree::MerkleTreeMmcs;
use p3_multilinear_util::point::Point;
use p3_sumcheck::layout::{Layout, SuffixProver, Table};
use p3_sumcheck::{OpeningBatch, OpeningProtocol, PrescribedPointPcs, TableShape, TableSpec};
use p3_symmetric::{CompressionFunctionFromHasher, SerializingHasher};
use rand::rngs::SmallRng;
use rand::{RngExt, SeedableRng};
type EF = BinaryField128;
type MyHash = SerializingHasher<Keccak256Hash>;
type MyCompress = CompressionFunctionFromHasher<Keccak256Hash, 2, 32>;
type MyMmcs = MerkleTreeMmcs<EF, u8, MyHash, MyCompress, 2, 32>;
type MyChallenger = BinaryChallenger<EF, HashChallenger<u8, Keccak256Hash, 32>>;
const LOG_BITS: [usize; 3] = [16, 18, 20];
const ABSORBED: usize = 7;
const fn mmcs() -> MyMmcs {
MyMmcs::new(
MyHash::new(Keccak256Hash),
MyCompress::new(Keccak256Hash),
0,
)
}
const fn challenger() -> MyChallenger {
MyChallenger::from_hasher(Vec::new(), Keccak256Hash)
}
const fn params() -> BinaryPcsParams {
BinaryPcsParams {
log_inv_rate: 2,
pow_bits: 0,
security_level: 100,
}
}
fn witness(log_bits: usize) -> Vec<PackedGf2x64> {
let mut rng = SmallRng::seed_from_u64(0xB001);
(0..1 << (log_bits - 6))
.map(|_| PackedGf2x64::new(rng.random::<u64>()))
.collect()
}
fn embedded(bits: &[PackedGf2x64]) -> Vec<EF> {
bits.iter()
.flat_map(|block| {
(0..PackedGf2x64::WIDTH).map(move |lane| {
if block.get(lane) == Gf2::ONE {
EF::ONE
} else {
EF::ZERO
}
})
})
.collect()
}
fn boolean_pcs(log_bits: usize) -> BooleanPcs<EF, MyMmcs, MyMmcs> {
let config = BinaryPcsConfig::try_new::<EF, EF>(log_bits - ABSORBED, params()).unwrap();
BooleanPcs::new(config, mmcs(), mmcs(), log_bits).unwrap()
}
fn embedded_pcs(log_bits: usize) -> BinaryPcs<EF, EF, MyMmcs, MyMmcs> {
let config = BinaryPcsConfig::try_new::<EF, EF>(log_bits, params()).unwrap();
BinaryPcs::new(config, mmcs(), mmcs()).unwrap()
}
fn embedded_protocol(log_bits: usize) -> OpeningProtocol {
OpeningProtocol::new(vec![TableSpec::new(
TableShape::new(log_bits, 1),
vec![OpeningBatch::new(vec![0], Vec::new())],
)])
}
fn bench_commit(c: &mut Criterion) {
let mut group = c.benchmark_group("boolean_commit");
group.sample_size(10);
for &log_bits in &LOG_BITS {
let bits = witness(log_bits);
let packed = boolean_pcs(log_bits);
group.bench_with_input(BenchmarkId::new("packed", log_bits), &bits, |b, bits| {
b.iter(|| packed.commit_bits(bits, &mut challenger()).unwrap());
});
let plain = embedded_pcs(log_bits);
let cells = embedded(&bits);
group.bench_with_input(
BenchmarkId::new("embedded", log_bits),
&cells,
|b, cells| {
b.iter(|| {
let table = Table::new(RowMajorMatrix::new(cells.clone(), cells.len()));
let witness = SuffixProver::<EF, EF>::new_witness(vec![table], 0);
plain.commit(witness, &mut challenger()).unwrap()
});
},
);
}
group.finish();
}
fn bench_open(c: &mut Criterion) {
let mut group = c.benchmark_group("boolean_open");
group.sample_size(10);
for &log_bits in &LOG_BITS {
let bits = witness(log_bits);
let point = Point::<EF>::rand(&mut SmallRng::seed_from_u64(0xB002), log_bits);
let packed = boolean_pcs(log_bits);
let mut chal = challenger();
let (_, data) = packed.commit_bits(&bits, &mut chal).unwrap();
let points = vec![point.clone()];
let (_, proof) = packed
.open_at_points(data.clone(), &points, &mut chal.clone())
.unwrap();
eprintln!(
"boolean/proof_size_packed/{log_bits}: {} bytes",
postcard::to_allocvec(&proof).unwrap().len()
);
group.bench_with_input(BenchmarkId::new("packed", log_bits), &point, |b, point| {
b.iter_batched(
|| (data.clone(), chal.clone()),
|(data, mut chal)| {
packed
.open_at_points(data, core::slice::from_ref(point), &mut chal)
.unwrap()
},
criterion::BatchSize::PerIteration,
);
});
let plain = embedded_pcs(log_bits);
let protocol = embedded_protocol(log_bits);
let cells = embedded(&bits);
let table = Table::new(RowMajorMatrix::new(cells.clone(), cells.len()));
let mut plain_chal = challenger();
let (_, plain_data) = plain
.commit(
SuffixProver::<EF, EF>::new_witness(vec![table], 0),
&mut plain_chal,
)
.unwrap();
let plain_proof = plain
.open_at(
plain_data.clone(),
&protocol,
core::slice::from_ref(&point),
&mut plain_chal.clone(),
)
.unwrap();
eprintln!(
"boolean/proof_size_embedded/{log_bits}: {} bytes",
postcard::to_allocvec(&plain_proof).unwrap().len()
);
group.bench_with_input(
BenchmarkId::new("embedded", log_bits),
&point,
|b, point| {
b.iter_batched(
|| (plain_data.clone(), plain_chal.clone()),
|(data, mut chal)| {
plain
.open_at(data, &protocol, core::slice::from_ref(point), &mut chal)
.unwrap()
},
criterion::BatchSize::PerIteration,
);
},
);
}
group.finish();
}
fn bench_batched(c: &mut Criterion) {
const NUM_POINTS: usize = 4;
let mut group = c.benchmark_group("boolean_batched");
group.sample_size(10);
for &log_bits in &LOG_BITS {
let bits = witness(log_bits);
let pcs = boolean_pcs(log_bits);
let mut rng = SmallRng::seed_from_u64(0xB003);
let points: Vec<Point<EF>> = (0..NUM_POINTS)
.map(|_| Point::<EF>::rand(&mut rng, log_bits))
.collect();
let mut chal = challenger();
let (_, data) = pcs.commit_bits(&bits, &mut chal).unwrap();
let (_, batched) = pcs
.open_at_points(data.clone(), &points, &mut chal.clone())
.unwrap();
let separate: usize = points
.iter()
.map(|point| {
let (_, proof) = pcs
.open_at_points(
data.clone(),
core::slice::from_ref(point),
&mut chal.clone(),
)
.unwrap();
postcard::to_allocvec(&proof).unwrap().len()
})
.sum();
eprintln!(
"boolean/batched_size/{log_bits}: {} bytes vs {separate} separate",
postcard::to_allocvec(&batched).unwrap().len(),
);
group.bench_with_input(
BenchmarkId::new("batched", log_bits),
&points,
|b, points| {
b.iter_batched(
|| (data.clone(), chal.clone()),
|(data, mut chal)| pcs.open_at_points(data, points, &mut chal).unwrap(),
criterion::BatchSize::PerIteration,
);
},
);
group.bench_with_input(
BenchmarkId::new("separate", log_bits),
&points,
|b, points| {
b.iter_batched(
|| (data.clone(), chal.clone()),
|(data, chal)| {
for point in points {
let _ = pcs
.open_at_points(
data.clone(),
core::slice::from_ref(point),
&mut chal.clone(),
)
.unwrap();
}
},
criterion::BatchSize::PerIteration,
);
},
);
}
group.finish();
}
criterion_group!(benches, bench_commit, bench_open, bench_batched);
criterion_main!(benches);