Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce benchmark startup time and StarkNet field #654

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions bench-templates/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ark-ec.workspace = true
ark-ff.workspace = true
ark-serialize.workspace = true
paste.workspace = true
rayon.workspace = true

[features]
asm = [ "ark-ff/asm" ]
Expand Down
2 changes: 2 additions & 0 deletions bench-templates/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ pub extern crate criterion;
pub use criterion::*;

pub use paste::paste;

pub use rayon;
45 changes: 26 additions & 19 deletions bench-templates/src/macros/ec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,32 @@ macro_rules! ec_bench {
use ark_ff::AdditiveGroup;
use ark_ec::{CurveGroup, PrimeGroup};
use ark_std::UniformRand;
use $crate::rayon::prelude::*;
let name = format!("{}::{}", $curve_name, stringify!($Group));

type Scalar = <$Group as PrimeGroup>::ScalarField;
const SAMPLES: usize = 1000;
let mut rng = ark_std::test_rng();
let mut arithmetic =
c.benchmark_group(format!("Arithmetic for {name}"));
// Faster sampling of inputs for benchmarking
let group_elements_left = (0..SAMPLES)
.map(|_| <$Group>::rand(&mut rng))
.into_par_iter()
.map(|_| <$Group>::rand(&mut ark_std::rand::thread_rng()))
.collect::<Vec<_>>();
let group_elements_right = (0..SAMPLES)
.map(|_| <$Group>::rand(&mut rng))
let mut group_elements_right = (0..SAMPLES)
.into_par_iter()
.map(|_| <$Group>::rand(&mut ark_std::rand::thread_rng()))
.collect::<Vec<_>>();
group_elements_right.reverse();
let group_elements_right_affine = <$Group>::normalize_batch(&group_elements_right);

// Sample scalars
let scalars = (0..SAMPLES)
.map(|_| Scalar::rand(&mut rng))
.collect::<Vec<_>>();

// Conduct benchmarks
arithmetic.bench_function("Addition", |b| {
let mut i = 0;
b.iter(|| {
Expand Down Expand Up @@ -99,7 +108,7 @@ macro_rules! ec_bench {
}

fn serialization(c: &mut $crate::criterion::Criterion) {
use ark_ec::CurveGroup;
use ark_ec::{AdditiveGroup, CurveGroup};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::UniformRand;

Expand All @@ -108,9 +117,9 @@ macro_rules! ec_bench {
let name = format!("{}::{}", $curve_name, stringify!($Group));
let mut rng = ark_std::test_rng();

let v: Vec<_> = (0..SAMPLES)
.map(|_| <$Group>::rand(&mut rng))
.collect();
// Efficient sampling of inputs for benchmarking
let g = <$Group>::rand(&mut rng);
let v: Vec<_> = (0..SAMPLES).map(|_| g.double()).collect();
let v = <$Group>::normalize_batch(&v);
let mut bytes = Vec::with_capacity(1000);
let v_compressed = v
Expand All @@ -129,6 +138,7 @@ macro_rules! ec_bench {
bytes
})
.collect::<Vec<_>>();
// Start benchmarks
let mut serialization =
c.benchmark_group(format!("Serialization for {name}"));
serialization.bench_function(
Expand Down Expand Up @@ -205,7 +215,7 @@ macro_rules! ec_bench {
}

fn msm_131072(c: &mut $crate::criterion::Criterion) {
use ark_ec::{scalar_mul::variable_base::VariableBaseMSM, CurveGroup};
use ark_ec::{scalar_mul::variable_base::VariableBaseMSM, CurveGroup, AdditiveGroup};
use ark_ff::PrimeField;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::UniformRand;
Expand All @@ -215,18 +225,15 @@ macro_rules! ec_bench {
let name = format!("{}::{}", $curve_name, stringify!($Group));
let mut rng = ark_std::test_rng();

let v: Vec<_> = (0..SAMPLES)
.map(|_| <$Group>::rand(&mut rng))
.collect();
let v = <$Group>::normalize_batch(&v);
let scalars: Vec<_> = (0..SAMPLES)
.map(|_| Scalar::rand(&mut rng).into_bigint())
.collect();
c.bench_function(&format!("MSM for {name}"), |b| {
b.iter(|| {
let result: $Group = VariableBaseMSM::msm_bigint(&v, &scalars);
result
})
let g = <$Group>::rand(&mut rng);
let v: Vec<_> = (0..SAMPLES).map(|_| g.double()).collect();
let v = <$Group>::normalize_batch(&v);

let scalars: Vec<_> = (0..SAMPLES)
.map(|_| Scalar::rand(&mut rng).into_bigint())
.collect();
b.iter(|| <$Group>::msm_bigint(&v, &scalars))
});
}

Expand Down
18 changes: 10 additions & 8 deletions bench-templates/src/macros/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,22 +340,24 @@ macro_rules! prime_field {
v2[i].num_bits()
})
});
let v_bits_le = v2
.iter()
.map(|s| ark_ff::BitIteratorLE::new(s).collect::<Vec<_>>())
.collect::<Vec<_>>();

bits.bench_function("From Little-Endian bits", |b| {
let v_bits_le = v2
.iter()
.map(|s| ark_ff::BitIteratorLE::new(s).collect::<Vec<_>>())
.collect::<Vec<_>>();
let mut i = 0;
b.iter(|| {
i = (i + 1) % SAMPLES;
BigInt::from_bits_be(&v_bits_le[i]);
})
});
let v_bits_be = v1
.iter()
.map(|s| ark_ff::BitIteratorBE::new(s).collect::<Vec<_>>())
.collect::<Vec<_>>();

bits.bench_function("From Big-Endian bits", |b| {
let v_bits_be = v1
.iter()
.map(|s| ark_ff::BitIteratorBE::new(s).collect::<Vec<_>>())
.collect::<Vec<_>>();
let mut i = 0;
b.iter(|| {
i = (i + 1) % SAMPLES;
Expand Down
24 changes: 18 additions & 6 deletions bench-templates/src/macros/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ macro_rules! bench {

paste! {
criterion_main!(
[<$G1:lower>]::benches,
[<$G2:lower>]::benches,
[<$Fr:lower>]::benches,
[<$Fq:lower>]::benches,
[<$FqExt:lower>]::benches,
[<$FqTarget:lower>]::benches,
[<$G1:lower>]::benches,
[<$G2:lower>]::benches,
pairing::benches
);
}
Expand All @@ -45,15 +45,15 @@ macro_rules! bench {
ScalarField = $Fr:ident,
BaseField = $Fq:ident,
) => {
$crate::ec_bench!($name, $G);
$crate::f_bench!(prime, $name, $Fr);
$crate::f_bench!(extension, $name, $Fq);
$crate::ec_bench!($name, $G);

paste! {
criterion_main!(
[<$G:lower>]::benches,
[<$Fr:lower>]::benches,
[<$Fq:lower>]::benches,
[<$G:lower>]::benches,
);
}
};
Expand All @@ -63,15 +63,27 @@ macro_rules! bench {
ScalarField = $Fr:ident,
PrimeBaseField = $Fq:ident,
) => {
$crate::ec_bench!($name, $G);
$crate::f_bench!(prime, $name, $Fr);
$crate::f_bench!(prime, $name, $Fq);
$crate::ec_bench!($name, $G);

paste! {
criterion_main!(
[<$G:lower>]::benches,
[<$Fr:lower>]::benches,
[<$Fq:lower>]::benches,
[<$G:lower>]::benches,
);
}
};
(
Name = $name:expr,
PrimeField = $Fp:ident,
) => {
$crate::f_bench!(prime, $name, $Fp);

paste! {
criterion_main!(
[<$Fp:lower>]::benches,
);
}
};
Expand Down
52 changes: 37 additions & 15 deletions ff/src/fields/fft_friendly.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use num_bigint::BigUint;

/// The interface for fields that are able to be used in FFTs.
pub trait FftField: crate::Field {
/// The generator of the multiplicative group of the field
Expand Down Expand Up @@ -31,21 +33,22 @@
/// of order n for the larger subgroup generated by
/// `FftConfig::LARGE_SUBGROUP_ROOT_OF_UNITY`
/// (for n = 2^i * FftConfig::SMALL_SUBGROUP_BASE^j for some i, j).
fn get_root_of_unity(n: u64) -> Option<Self> {
fn get_root_of_unity(n: impl Into<BigUint>) -> Option<Self> {
let mut omega: Self;
if let Some(large_subgroup_root_of_unity) = Self::LARGE_SUBGROUP_ROOT_OF_UNITY {
let q = Self::SMALL_SUBGROUP_BASE.expect(
"LARGE_SUBGROUP_ROOT_OF_UNITY should only be set in conjunction with SMALL_SUBGROUP_BASE",
) as u64;
let small_subgroup_base_adicity = Self::SMALL_SUBGROUP_BASE_ADICITY.expect(
"LARGE_SUBGROUP_ROOT_OF_UNITY should only be set in conjunction with SMALL_SUBGROUP_BASE_ADICITY",
);

let q_adicity = crate::utils::k_adicity(q, n);
let q_part = q.checked_pow(q_adicity)?;
let n = n.into();
let two = BigUint::from(2u64);
let max_subgroup_size = Self::max_subgroup_size();
if n > max_subgroup_size {
return None;
} else if let Some(large_subgroup_root) = Self::LARGE_SUBGROUP_ROOT_OF_UNITY {
let q = Self::SMALL_SUBGROUP_BASE?;
let small_subgroup_base_adicity = Self::SMALL_SUBGROUP_BASE_ADICITY?;
let q_adicity = crate::utils::k_adicity(q as u64, &n);
let q: BigUint = q.into();
let q_part = q.pow(q_adicity);

let two_adicity = crate::utils::k_adicity(2, n);
let two_part = 2u64.checked_pow(two_adicity)?;
let two_adicity = crate::utils::k_adicity(2, &n);
let two_part = two.pow(two_adicity);

if n != two_part * q_part
|| (two_adicity > Self::TWO_ADICITY)
Expand All @@ -54,9 +57,9 @@
return None;
}

omega = large_subgroup_root_of_unity;
omega = large_subgroup_root;
for _ in q_adicity..small_subgroup_base_adicity {
omega = omega.pow([q as u64]);
omega = omega.pow(q.to_u64_digits());
}

for _ in two_adicity..Self::TWO_ADICITY {
Expand All @@ -64,7 +67,7 @@
}
} else {
// Compute the next power of 2.
let size = n.next_power_of_two() as u64;

Check failure on line 70 in ff/src/fields/fft_friendly.rs

View workflow job for this annotation

GitHub Actions / Check no_std

no method named `next_power_of_two` found for struct `BigUint` in the current scope

Check failure on line 70 in ff/src/fields/fft_friendly.rs

View workflow job for this annotation

GitHub Actions / Test (stable)

no method named `next_power_of_two` found for struct `BigUint` in the current scope

Check failure on line 70 in ff/src/fields/fft_friendly.rs

View workflow job for this annotation

GitHub Actions / Test assembly

no method named `next_power_of_two` found for struct `BigUint` in the current scope
let log_size_of_group = ark_std::log2(usize::try_from(size).expect("too large"));

if n != size || log_size_of_group > Self::TWO_ADICITY {
Expand All @@ -80,4 +83,23 @@
}
Some(omega)
}

fn max_subgroup_size() -> BigUint {
let two = BigUint::from(2u64);
let two_part = two.pow(Self::TWO_ADICITY);
if Self::SMALL_SUBGROUP_BASE.is_none() {
two_part
} else {
let (q, q_adicity) = Self::SMALL_SUBGROUP_BASE
.and_then(|q| {
let q_adicity = Self::SMALL_SUBGROUP_BASE_ADICITY?;
Some((BigUint::from(q), q_adicity))
})
.expect(
"LARGE_SUBGROUP_ROOT_OF_UNITY should only be set in conjunction with SMALL_SUBGROUP_BASE",
);
let q_part = q.pow(q_adicity);
q_part * two_part
}
}
}
13 changes: 9 additions & 4 deletions ff/src/fields/utils.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
use num_bigint::BigUint;
use num_traits::{One, Zero};

/// Calculates the k-adicity of n, i.e., the number of trailing 0s in a base-k
/// representation.
pub fn k_adicity(k: u64, mut n: u64) -> u32 {
pub fn k_adicity(k: u64, mut n: &BigUint) -> u32 {
let mut n = n.clone();
let one = BigUint::one();
let mut r = 0;
while n > 1 {
if n % k == 0 {
r += 1;
while n > one {
if (n % k).is_zero() {
r += 1u32;
n /= k;
} else {
return r;
Expand Down
5 changes: 5 additions & 0 deletions test-curves/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,8 @@ harness = false
name = "mnt6_753"
path = "benches/mnt6_753.rs"
harness = false

[[bench]]
name = "starknet_fp"
path = "benches/starknet_fp.rs"
harness = false
4 changes: 4 additions & 0 deletions test-curves/benches/starknet_fp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
use ark_algebra_bench_templates::*;
use ark_test_curves::starknet_fp::Fq;

bench!(Name = "StarkNetFp", PrimeField = Fq,);
2 changes: 2 additions & 0 deletions test-curves/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ pub mod bn384_small_two_adicity;
pub mod secp256k1;

pub mod fp128;

pub mod starknet_fp;
15 changes: 15 additions & 0 deletions test-curves/src/starknet_fp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//! Prime field `Fq` where `q = 2^251 + 17 * 2^192 + 1` is the StarkNet prime.
use ark_ff::fields::{Fp256, MontBackend};

#[derive(ark_ff::MontConfig)]
#[modulus = "3618502788666131213697322783095070105623107215331596699973092056135872020481"]
#[generator = "3"]
pub struct FqConfig;
pub type Fq = Fp256<MontBackend<FqConfig, 4>>;

#[cfg(test)]
mod tests {
use super::*;
use ark_algebra_test_templates::*;
test_field!(fp; Fq; mont_prime_field);
}
7 changes: 5 additions & 2 deletions test-templates/src/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,9 +359,12 @@ macro_rules! __test_field {
#[test]
fn test_fft() {
use ark_ff::FftField;
use $crate::num_bigint::BigUint;
let two_adic_pow = (BigUint::one() << <$field>::TWO_ADICITY).to_u64_digits();
assert_eq!(
<$field>::TWO_ADIC_ROOT_OF_UNITY.pow([1 << <$field>::TWO_ADICITY]),
<$field>::one()
<$field>::TWO_ADIC_ROOT_OF_UNITY.pow(two_adic_pow),
<$field>::one(),
"2-adicity is incorrect or 2-adic root of unity is invalid"
);

if let Some(small_subgroup_base) = <$field>::SMALL_SUBGROUP_BASE {
Expand Down
Loading