Skip to content

Commit

Permalink
Merge pull request #2338 from ljedrz/bench/fields
Browse files Browse the repository at this point in the history
Introduce field-related benches
  • Loading branch information
howardwu authored Feb 9, 2024
2 parents 8b00228 + f53fddd commit e585e64
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 31 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions fields/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ include = [ "Cargo.toml", "src", "README.md", "LICENSE.md" ]
license = "Apache-2.0"
edition = "2021"

[[bench]]
name = "fields"
path = "benches/fields.rs"
harness = false

[dependencies.snarkvm-utilities]
path = "../utilities"
version = "=0.16.18"
Expand Down Expand Up @@ -60,6 +65,14 @@ version = "1.0"
version = "1"
features = [ "derive" ]

[dev-dependencies.criterion]
version = "0.5"

[dev-dependencies.snarkvm-curves]
path = "../curves"
version = "=0.16.18"
default-features = false

[features]
default = [ "snarkvm-utilities/default" ]
profiler = [ "aleo-std/profiler" ]
Expand Down
163 changes: 163 additions & 0 deletions fields/benches/fields.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
// Copyright (C) 2019-2023 Aleo Systems Inc.
// This file is part of the snarkVM library.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Silences the false positives caused by black_box.
#![allow(clippy::unit_arg)]

use snarkvm_fields::{Field, Fp256, Fp384, PrimeField};
use snarkvm_utilities::TestRng;

use criterion::*;
use rand::Rng;
use std::{
hint::black_box,
ops::{AddAssign, MulAssign, SubAssign},
};

fn fp256(c: &mut Criterion) {
let rng = &mut TestRng::default();

const N: usize = 1_000;

let mut values = Vec::with_capacity(N);
let mut arr = [0u8; 32];

while values.len() < N {
rng.fill(&mut arr);
if let Some(v) = Fp256::<snarkvm_curves::bls12_377::FrParameters>::from_random_bytes(&arr) {
values.push(v);
}
}

c.bench_function("fp256_add_assign", |b| {
b.iter(|| {
for (mut v1, v2) in values.iter().cloned().zip(values.iter().skip(1)) {
black_box(v1.add_assign(v2));
}
})
});

c.bench_function("fp256_sub_assign", |b| {
b.iter(|| {
for (mut v1, v2) in values.iter().cloned().zip(values.iter().skip(1)) {
black_box(v1.sub_assign(v2));
}
})
});

c.bench_function("fp256_mul_assign", |b| {
b.iter(|| {
for (mut v1, v2) in values.iter().cloned().zip(values.iter().skip(1)) {
black_box(v1.mul_assign(v2));
}
})
});

c.bench_function("fp256_inverse", |b| {
b.iter(|| {
for v in values.iter() {
black_box(v.inverse());
}
})
});

c.bench_function("fp256_square_in_place", |b| {
b.iter(|| {
for mut v in values.iter().cloned() {
black_box(v.square_in_place());
}
})
});

c.bench_function("fp256_to_bigint", |b| {
b.iter(|| {
for v in values.iter() {
black_box(v.to_bigint());
}
})
});
}

fn fp384(c: &mut Criterion) {
let rng = &mut TestRng::default();

const N: usize = 1_000;

let mut values = Vec::with_capacity(N);
let mut arr = [0u8; 48];

while values.len() < N {
rng.fill(&mut arr[..32]);
rng.fill(&mut arr[32..]);
if let Some(v) = Fp384::<snarkvm_curves::bls12_377::FqParameters>::from_random_bytes(&arr) {
values.push(v);
}
}

c.bench_function("fp384_add_assign", |b| {
b.iter(|| {
for (mut v1, v2) in values.iter().cloned().zip(values.iter().skip(1)) {
black_box(v1.add_assign(v2));
}
})
});

c.bench_function("fp384_sub_assign", |b| {
b.iter(|| {
for (mut v1, v2) in values.iter().cloned().zip(values.iter().skip(1)) {
black_box(v1.sub_assign(v2));
}
})
});

c.bench_function("fp384_mul_assign", |b| {
b.iter(|| {
for (mut v1, v2) in values.iter().cloned().zip(values.iter().skip(1)) {
black_box(v1.mul_assign(v2));
}
})
});

c.bench_function("fp384_inverse", |b| {
b.iter(|| {
for v in values.iter() {
black_box(v.inverse());
}
})
});

c.bench_function("fp384_square_in_place", |b| {
b.iter(|| {
for mut v in values.iter().cloned() {
black_box(v.square_in_place());
}
})
});

c.bench_function("fp384_to_bigint", |b| {
b.iter(|| {
for v in values.iter() {
black_box(v.to_bigint());
}
})
});
}

criterion_group! {
name = fields;
config = Criterion::default();
targets = fp256, fp384
}

criterion_main!(fields);
48 changes: 23 additions & 25 deletions fields/src/fp_256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ impl<P: Fp256Parameters> Fp256<P> {
impl<P: Fp256Parameters> Zero for Fp256<P> {
#[inline]
fn zero() -> Self {
Fp256::<P>(BigInteger::from(0), PhantomData)
Self(BigInteger::from(0), PhantomData)
}

#[inline]
Expand All @@ -134,12 +134,12 @@ impl<P: Fp256Parameters> Zero for Fp256<P> {
impl<P: Fp256Parameters> One for Fp256<P> {
#[inline]
fn one() -> Self {
Fp256::<P>(P::R, PhantomData)
Self(P::R, PhantomData)
}

#[inline]
fn is_one(&self) -> bool {
self == &Self::one()
self.0 == P::R
}
}

Expand Down Expand Up @@ -299,7 +299,7 @@ impl<P: Fp256Parameters> Field for Fp256<P> {

let mut u = self.0;
let mut v = P::MODULUS;
let mut b = Fp256::<P>(P::R2, PhantomData); // Avoids unnecessary reduction step.
let mut b = Self(P::R2, PhantomData); // Avoids unnecessary reduction step.
let mut c = Self::zero();

while u != one && v != one {
Expand Down Expand Up @@ -536,12 +536,25 @@ impl<P: Fp256Parameters> SquareRootField for Fp256<P> {
}

fn sqrt_in_place(&mut self) -> Option<&mut Self> {
if let Some(sqrt) = self.sqrt() {
(*self).sqrt().map(|sqrt| {
*self = sqrt;
Some(self)
} else {
None
}
self
})
}
}

/// `Fp` elements are ordered lexicographically.
impl<P: Fp256Parameters> Ord for Fp256<P> {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
self.to_bigint().cmp(&other.to_bigint())
}
}

impl<P: Fp256Parameters> PartialOrd for Fp256<P> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

Expand Down Expand Up @@ -593,21 +606,6 @@ impl<P: Fp256Parameters> FromBytes for Fp256<P> {
}
}

/// `Fp` elements are ordered lexicographically.
impl<P: Fp256Parameters> Ord for Fp256<P> {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
self.to_bigint().cmp(&other.to_bigint())
}
}

impl<P: Fp256Parameters> PartialOrd for Fp256<P> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl<P: Fp256Parameters> FromStr for Fp256<P> {
type Err = FieldError;

Expand Down Expand Up @@ -677,7 +675,7 @@ impl<P: Fp256Parameters> Neg for Fp256<P> {
if !self.is_zero() {
let mut tmp = P::MODULUS;
tmp.sub_noborrow(&self.0);
Fp256::<P>(tmp, PhantomData)
Self(tmp, PhantomData)
} else {
self
}
Expand Down
14 changes: 8 additions & 6 deletions fields/src/fp_384.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ impl<P: Fp384Parameters> Fp384<P> {
impl<P: Fp384Parameters> Zero for Fp384<P> {
#[inline]
fn zero() -> Self {
Fp384::<P>(BigInteger::from(0), PhantomData)
Self(BigInteger::from(0), PhantomData)
}

#[inline]
Expand All @@ -168,7 +168,7 @@ impl<P: Fp384Parameters> Zero for Fp384<P> {
impl<P: Fp384Parameters> One for Fp384<P> {
#[inline]
fn one() -> Self {
Fp384::<P>(P::R, PhantomData)
Self(P::R, PhantomData)
}

#[inline]
Expand Down Expand Up @@ -357,7 +357,7 @@ impl<P: Fp384Parameters> Field for Fp384<P> {

let mut u = self.0;
let mut v = P::MODULUS;
let mut b = Fp384::<P>(P::R2, PhantomData); // Avoids unnecessary reduction step.
let mut b = Self(P::R2, PhantomData); // Avoids unnecessary reduction step.
let mut c = Self::zero();

while u != one && v != one {
Expand Down Expand Up @@ -433,7 +433,6 @@ impl<P: Fp384Parameters> PrimeField for Fp384<P> {
let mut tmp = self.0;
let mut r = tmp.0;
// Montgomery Reduction
// Iteration 0
let k = r[0].wrapping_mul(P::INV);
let mut carry = 0;
fa::mac_with_carry(r[0], k, P::MODULUS.0[0], &mut carry);
Expand Down Expand Up @@ -538,6 +537,7 @@ impl<P: Fp384Parameters> SquareRootField for Fp384<P> {

// s = self^((MODULUS - 1) // 2)
let s = self.pow(P::MODULUS_MINUS_ONE_DIV_TWO);

if s.is_zero() {
Zero
} else if s.is_one() {
Expand All @@ -560,6 +560,7 @@ impl<P: Fp384Parameters> SquareRootField for Fp384<P> {
}
}

/// `Fp` elements are ordered lexicographically.
impl<P: Fp384Parameters> Ord for Fp384<P> {
#[inline(always)]
fn cmp(&self, other: &Self) -> Ordering {
Expand Down Expand Up @@ -691,7 +692,7 @@ impl<P: Fp384Parameters> Neg for Fp384<P> {
if !self.is_zero() {
let mut tmp = P::MODULUS;
tmp.sub_noborrow(&self.0);
Fp384::<P>(tmp, PhantomData)
Self(tmp, PhantomData)
} else {
self
}
Expand Down Expand Up @@ -747,7 +748,7 @@ impl<'a, P: Fp384Parameters> AddAssign<&'a Self> for Fp384<P> {
fn add_assign(&mut self, other: &Self) {
// This cannot exceed the backing capacity.
self.0.add_nocarry(&other.0);
// However, it may need to be reduced
// However, it may need to be reduced.
self.reduce();
}
}
Expand All @@ -771,6 +772,7 @@ impl<'a, P: Fp384Parameters> MulAssign<&'a Self> for Fp384<P> {
let mut carry1 = 0u64;
let mut carry2 = 0u64;

// Iteration 0.
r[0] = fa::mac(r[0], (self.0).0[0], (other.0).0[0], &mut carry1);
let k = r[0].wrapping_mul(P::INV);
fa::mac_discard(r[0], k, P::MODULUS.0[0], &mut carry2);
Expand Down

0 comments on commit e585e64

Please sign in to comment.