Skip to content

Commit

Permalink
feat(benchmarks): Refactor Field Benchmarks (#606)
Browse files Browse the repository at this point in the history
* add perf flamegraph and refactor bench

* fmt

* ci

* fix xi

* fix ci

---------

Co-authored-by: Mariano A. Nicolini <[email protected]>
  • Loading branch information
PatStiles and entropidelic authored Oct 17, 2023
1 parent a1f2fa7 commit d2184c0
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 100 deletions.
3 changes: 2 additions & 1 deletion math/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ lambdaworks-gpu = { workspace = true, optional = true }

[dev-dependencies]
rand = { version = "0.8.5", default-features = false }
criterion = "0.4"
criterion = "0.5.1"
const-random = "0.1.15"
iai-callgrind.workspace = true
proptest = "1.1.0"
pprof = { version = "0.13.0", features = ["criterion","flamegraph"] }

[features]
rayon = ["dep:rayon"]
Expand Down
108 changes: 9 additions & 99 deletions math/benches/criterion_field.rs
Original file line number Diff line number Diff line change
@@ -1,102 +1,12 @@
use std::hint::black_box;

use criterion::{criterion_group, criterion_main, Criterion};
use lambdaworks_math::{
field::fields::{
fft_friendly::stark_252_prime_field::{
MontgomeryConfigStark252PrimeField, Stark252PrimeField,
},
montgomery_backed_prime_fields::IsModulus,
},
unsigned_integer::{element::U256, montgomery::MontgomeryAlgorithms},
};

mod utils;
use utils::u64_utils;

pub fn starkfield_ops_benchmarks(c: &mut Criterion) {
let mut group = c.benchmark_group("Stark FP operations");
let (x, y) = u64_utils::get_field_elements();

group.bench_with_input("add", &(x, y), |bench, (x, y)| {
bench.iter(|| x + y);
});

group.bench_with_input("mul", &(x, y), |bench, (x, y)| {
bench.iter(|| x * y);
});

group.bench_with_input("pow by 1", &x, |bench, x| {
bench.iter(|| x.pow(1_u64));
});

// The non-boxed constants are intentional as they are
// normally computed at compile time.
group.bench_with_input("sos_square", &x, |bench, x| {
bench.iter(|| {
MontgomeryAlgorithms::sos_square(
black_box(x.value()),
&<MontgomeryConfigStark252PrimeField as IsModulus<U256>>::MODULUS,
&Stark252PrimeField::MU,
)
});
});

group.bench_with_input("square", &x, |bench, x| {
bench.iter(|| x.square());
});

group.bench_with_input("square with pow", &x, |bench, x| {
bench.iter(|| x.pow(2_u64));
});

group.bench_with_input("square with mul", &x, |bench, x| {
bench.iter(|| x * x);
});

group.bench_with_input("pow", &(x, 5u64), |bench, (x, y)| {
bench.iter(|| x.pow(*y));
});

group.bench_with_input("sub", &(x, y), |bench, (x, y)| {
bench.iter(|| x - y);
});

group.bench_with_input("inv", &x, |bench, x| {
bench.iter(|| x.inv().unwrap());
});

group.bench_with_input("div", &(x, y), |bench, (x, y)| {
bench.iter(|| x / y);
});

group.bench_with_input("eq", &(x, y), |bench, (x, y)| {
bench.iter(|| x == y);
});

group.bench_with_input("sqrt", &x, |bench, x| {
bench.iter(|| x.sqrt());
});

group.bench_with_input("sqrt squared", &(x * x), |bench, x| {
bench.iter(|| x.sqrt());
});

// Unsigned integer bitwise operations
let (x, y) = (x.value(), y.value());

group.bench_with_input("bitand", &(x, y), |bench, (&x, &y)| {
bench.iter(|| x & y);
});

group.bench_with_input("bitor", &(x, y), |bench, (&x, &y)| {
bench.iter(|| x | y);
});
use pprof::criterion::{Output, PProfProfiler};

group.bench_with_input("bitxor", &(x, y), |bench, (&x, &y)| {
bench.iter(|| x ^ y);
});
}
mod fields;
use fields::stark252::starkfield_ops_benchmarks;

criterion_group!(starkfp, starkfield_ops_benchmarks);
criterion_main!(starkfp);
criterion_group!(
name = field_benches;
config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
targets = starkfield_ops_benchmarks
);
criterion_main!(field_benches);
1 change: 1 addition & 0 deletions math/benches/fields/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod stark252;
230 changes: 230 additions & 0 deletions math/benches/fields/stark252.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
use std::hint::black_box;

use criterion::Criterion;
use lambdaworks_math::{
field::{
element::FieldElement,
fields::{
fft_friendly::stark_252_prime_field::{
MontgomeryConfigStark252PrimeField, Stark252PrimeField,
},
montgomery_backed_prime_fields::IsModulus,
},
},
unsigned_integer::{
element::{UnsignedInteger, U256},
montgomery::MontgomeryAlgorithms,
},
};
use rand::random;

pub type F = FieldElement<Stark252PrimeField>;

#[inline(never)]
#[no_mangle]
#[export_name = "util::rand_field_elements"]
pub fn rand_field_elements(num: usize) -> Vec<(F, F)> {
let mut result = Vec::with_capacity(num);
for _ in 0..result.capacity() {
let rand_a = UnsignedInteger { limbs: random() };
let rand_b = UnsignedInteger { limbs: random() };
result.push((F::new(rand_a), F::new(rand_b)));
}
result
}

pub fn starkfield_ops_benchmarks(c: &mut Criterion) {
let input: Vec<Vec<(F, F)>> = [1, 10, 100, 1000, 10000, 100000, 1000000]
.into_iter()
.map(rand_field_elements)
.collect::<Vec<_>>();
let mut group = c.benchmark_group("Stark FP operations");

for i in input.clone().into_iter() {
group.bench_with_input(format!("add {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, y) in i {
black_box(black_box(x) + black_box(y));
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("mul {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, y) in i {
black_box(black_box(x) * black_box(y));
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("pow by 1 {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, _) in i {
black_box(black_box(x).pow(1_u64));
}
});
});
}

// The non-boxed constants are intentional as they are
// normally computed at compile time.
for i in input.clone().into_iter() {
group.bench_with_input(format!("sos_square {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, _) in i {
MontgomeryAlgorithms::sos_square(
black_box(black_box(x.value())),
&<MontgomeryConfigStark252PrimeField as IsModulus<U256>>::MODULUS,
&Stark252PrimeField::MU,
);
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, _) in i {
black_box(black_box(x).square());
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("square with pow {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, _) in i {
black_box(black_box(x).pow(2_u64));
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("square with mul {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, _) in i {
black_box(black_box(x) * black_box(x));
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(
format!("pow {:?}", &i.len()),
&(i, 5u64),
|bench, (i, a)| {
bench.iter(|| {
for (x, _) in i {
black_box(black_box(x).pow(*a));
}
});
},
);
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, y) in i {
black_box(black_box(x) - black_box(y));
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("inv {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, _) in i {
black_box(black_box(x).inv().unwrap());
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("div {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, y) in i {
black_box(black_box(x) / black_box(y));
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("eq {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, y) in i {
black_box(black_box(x) == black_box(y));
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("sqrt {:?}", &i.len()), &i, |bench, i| {
bench.iter(|| {
for (x, _) in i {
black_box(black_box(x).sqrt());
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("sqrt squared {:?}", &i.len()), &i, |bench, i| {
let i: Vec<F> = i.iter().map(|(x, _)| x * x).collect();
bench.iter(|| {
for x in &i {
black_box(black_box(x).sqrt());
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("bitand {:?}", &i.len()), &i, |bench, i| {
// Note: we should strive to have the number of limbs be generic... ideally this benchmark group itself should have a generic type that we call into from the main runner.
let i: Vec<(UnsignedInteger<4>, UnsignedInteger<4>)> =
i.iter().map(|(x, y)| (*x.value(), *y.value())).collect();
bench.iter(|| {
for (x, y) in &i {
black_box(black_box(*x) & black_box(*y));
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("bitor {:?}", &i.len()), &i, |bench, i| {
let i: Vec<(UnsignedInteger<4>, UnsignedInteger<4>)> =
i.iter().map(|(x, y)| (*x.value(), *y.value())).collect();
bench.iter(|| {
for (x, y) in &i {
black_box(black_box(*x) | black_box(*y));
}
});
});
}

for i in input.clone().into_iter() {
group.bench_with_input(format!("bitxor {:?}", &i.len()), &i, |bench, i| {
let i: Vec<(UnsignedInteger<4>, UnsignedInteger<4>)> =
i.iter().map(|(x, y)| (*x.value(), *y.value())).collect();
bench.iter(|| {
for (x, y) in &i {
black_box(black_box(*x) ^ black_box(*y));
}
});
});
}
}

0 comments on commit d2184c0

Please sign in to comment.