Skip to content

Tristan/war 540 sylow initial performance optimization #27

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

Merged
merged 8 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ fn main() -> Result<(), confy::ConfyError> {
tracing_subscriber::fmt().init();
event!(Level::INFO, "Begin dkg::main");
let config_path = PathBuf::from("examples/dkg.toml");
let cfg: MyConfig = confy::load_path(&config_path)?;
let cfg: MyConfig = confy::load_path(config_path)?;
event!(Level::INFO, "Loaded config: {:?}", cfg);

const NUMBER_OF_ROUNDS: u64 = 3;
Expand Down
10 changes: 8 additions & 2 deletions src/fields/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ impl<'a, 'b, const D: usize, const N: usize, F: FieldExtensionTrait<D, N>>
Add<&'b FieldExtension<D, N, F>> for &'a FieldExtension<D, N, F>
{
type Output = FieldExtension<D, N, F>;

#[inline]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, interesting, didn't know this existed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah its not exactly what you might expect from inline in c++. in debug mode, its basically totally ignore by the compiler, and in release mode, the compiler will codegen an #[inline] function into every single referencing codegen unit, and then it will also add inlinehint. This means that if you have 16 CGUs and they all reference an item, every single one is getting the entire item's implementation inlined into it, which isn't exactly the same as c++

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an excellent overview is given here rust-lang/hashbrown#119

fn add(self, other: &'b FieldExtension<D, N, F>) -> Self::Output {
let mut i = 0;
let mut retval = [F::zero(); N];
Expand All @@ -87,13 +87,15 @@ impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Add<FieldExte
for FieldExtension<D, N, F>
{
type Output = Self;
#[inline]
fn add(self, other: FieldExtension<D, N, F>) -> Self::Output {
&self + &other
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> AddAssign
for FieldExtension<D, N, F>
{
#[inline]
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
Expand All @@ -102,7 +104,7 @@ impl<'a, 'b, const D: usize, const N: usize, F: FieldExtensionTrait<D, N>>
Sub<&'b FieldExtension<D, N, F>> for &'a FieldExtension<D, N, F>
{
type Output = FieldExtension<D, N, F>;

#[inline]
fn sub(self, other: &'b FieldExtension<D, N, F>) -> Self::Output {
let mut i = 0;
let mut retval = [F::zero(); N];
Expand All @@ -117,13 +119,15 @@ impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Sub<FieldExte
for FieldExtension<D, N, F>
{
type Output = Self;
#[inline]
fn sub(self, other: FieldExtension<D, N, F>) -> Self::Output {
&self - &other
}
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> SubAssign
for FieldExtension<D, N, F>
{
#[inline]
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
Expand All @@ -145,6 +149,7 @@ impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> PartialEq
}
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Neg for FieldExtension<D, N, F> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
let mut i = 0;
let mut retval = [F::zero(); N];
Expand All @@ -158,6 +163,7 @@ impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Neg for Field
impl<const D: usize, const N: usize, F: FieldExtensionTrait<D, N>> Zero
for FieldExtension<D, N, F>
{
#[inline]
fn zero() -> Self {
Self::new(&[F::zero(); N])
}
Expand Down
58 changes: 46 additions & 12 deletions src/fields/fp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ pub(crate) const BN254_FP_MODULUS: Fp = Fp::new(U256::from_words([
0xB85045B68181585D,
0x30644E72E131A029,
]));
pub(crate) const FP_QUADRATIC_NON_RESIDUE: Fp = Fp::new(U256::from_words([
4332616871279656262,
10917124144477883021,
13281191951274694749,
3486998266802970665,
]));
/// This defines the key properties of a field extension. Now, mathematically,
/// a finite field satisfies many rigorous mathematical properties. The
/// (non-exhaustive) list below simply suffices to illustrate those properties
Expand Down Expand Up @@ -83,9 +89,6 @@ pub trait FieldExtensionTrait<const D: usize, const N: usize>:
+ Inv<Output = Self>
+ From<u64>
{
/// multiplication in a field extension is dictated heavily such a value below,
/// so we define the quadratic non-residue here
fn quadratic_non_residue() -> Self;
/// generate a random value in the field extension based on the random number generator from
/// `crypto_bigint`
fn rand<R: CryptoRngCore>(rng: &mut R) -> Self;
Expand Down Expand Up @@ -200,11 +203,6 @@ macro_rules! define_finite_prime_field {
// appropriate degree, in our case degree 1 (with
// therefore 1 unique representation of an element)
impl FieldExtensionTrait<$degree, $nreps> for $wrapper_name {
fn quadratic_non_residue() -> Self {
//this is p - 1 mod p = -1 mod p = 0 - 1 mod p
// = -1
Self::new((-Self::ONE).1.retrieve())
}
/// Generate a random value in the field
/// # Arguments
/// * `rng` - R: CryptoRngCore - the random number generator to use
Expand Down Expand Up @@ -233,11 +231,13 @@ macro_rules! define_finite_prime_field {
/// of the field element. All binops with assignment equivalents are given
impl Add for $wrapper_name {
type Output = Self;
#[inline]
fn add(self, other: Self) -> Self {
Self::new((self.1 + other.1).retrieve())
}
}
impl AddAssign for $wrapper_name {
#[inline]
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
Expand All @@ -262,11 +262,14 @@ macro_rules! define_finite_prime_field {
}
impl Sub for $wrapper_name {
type Output = Self;

#[inline]
fn sub(self, other: Self) -> Self {
Self::new((self.1 - other.1).retrieve())
}
}
impl SubAssign for $wrapper_name {
#[inline]
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
Expand Down Expand Up @@ -301,11 +304,13 @@ macro_rules! define_finite_prime_field {
}
impl Mul for $wrapper_name {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self {
Self::new((self.1 * other.1).retrieve())
}
}
impl MulAssign for $wrapper_name {
#[inline]
fn mul_assign(&mut self, other: Self) {
*self = *self * other;
}
Expand All @@ -324,30 +329,36 @@ macro_rules! define_finite_prime_field {
/// <https://github.com/RustCrypto/crypto-bigint/blob/be6a3abf7e65279ba0b5e4b1ce09eb0632e443f6/src/const_choice.rs#L237>
impl Inv for $wrapper_name {
type Output = Self;
#[inline]
fn inv(self) -> Self {
Self::new((CtOption::from(self.1.inv()).unwrap_or(Self::from(0u64).1)).retrieve())
}
}
#[allow(clippy::suspicious_arithmetic_impl)]
impl Div for $wrapper_name {
type Output = Self;
#[inline]
fn div(self, other: Self) -> Self {
self * other.inv()
}
}
impl DivAssign for $wrapper_name {
#[inline]
fn div_assign(&mut self, other: Self) {
*self = *self / other;
}
}
impl Neg for $wrapper_name {
type Output = Self;

#[inline]
fn neg(self) -> Self {
Self::new((-self.1).retrieve())
}
}
impl Pow<U256> for $wrapper_name {
type Output = Self;
#[inline]
fn pow(self, rhs: U256) -> Self::Output {
Self::new(self.1.pow(&rhs).retrieve())
}
Expand All @@ -360,6 +371,7 @@ macro_rules! define_finite_prime_field {
/// which we unwrap. Otherwise, there will be panic.
impl Rem for $wrapper_name {
type Output = Self;
#[inline]
fn rem(self, other: Self) -> Self::Output {
// create our own check for zeroness?
Self::new(
Expand All @@ -370,6 +382,7 @@ macro_rules! define_finite_prime_field {
}
}
impl Euclid for $wrapper_name {
#[inline]
fn div_euclid(&self, other: &Self) -> Self {
if other.is_zero() {
return Self::from(0u64);
Expand All @@ -385,6 +398,7 @@ macro_rules! define_finite_prime_field {
}
Self::new(_q)
}
#[inline]
fn rem_euclid(&self, other: &Self) -> Self {
if other.is_zero() {
return Self::from(0u64);
Expand Down Expand Up @@ -456,6 +470,7 @@ impl Fp {
/// function is inherently expensive, and we never call it on the base field, but if
/// we did, it's only defined for p=1. Specialized versions exist for all extensions which
/// will require the frobenius transformation.
#[inline(always)]
pub fn frobenius(&self, exponent: usize) -> Self {
match exponent {
1 => self.pow(BN254_FP_MODULUS.value()),
Expand All @@ -470,13 +485,15 @@ impl Fp {
/// prime that is congruent to 3 mod 4. In this case, the sqrt only has the
/// possible solution of $\pm pow(n, \frac{p+1}{4})$, which is where this magic
/// number below comes from ;)
#[inline]
pub fn sqrt(&self) -> CtOption<Self> {
let arg = ((Self::new(Self::characteristic()) + Self::one()) / Self::from(4)).value();
let sqrt = self.pow(arg);
tracing::debug!(?arg, ?sqrt, "Fp::sqrt");
CtOption::new(sqrt, sqrt.square().ct_eq(self))
}
/// Returns the square of the element in the base field
#[inline]
pub fn square(&self) -> Self {
(*self) * (*self)
}
Expand All @@ -491,14 +508,34 @@ impl Fp {
/// Determines the 'sign' of a value in the base field,
/// see <https://datatracker.ietf.org/doc/html/rfc9380#section-4.1> for more details
pub fn sgn0(&self) -> Choice {
let a = *self % Self::from(2u64);
let a = *self % Self::TWO;
tracing::debug!(?a, "Fp::sgn0");
if a.is_zero() {
Choice::from(0u8)
} else {
Choice::from(1u8)
}
}
/// There is a need to at times move to a representation of the field element with
/// a lower Hamming weight, for instance in the case of multiplication of a group element by
/// such a scalar. This implements the prodinger algorithm, and returns a string of the
/// positive bits and a string of negative bits for the NAF representation
/// see <http://math.colgate.edu/~integers/a8/a8.pdf>
pub(crate) fn compute_naf(self) -> (U256, U256) {
let x = self.value();
let xh = x >> 1;
let x3 = x + xh;
let c = xh ^ x3;
let np = x3 & c;
let nm = xh & c;

(np, nm)
}
}
impl Fr {
pub(crate) fn compute_naf(self) -> (U256, U256) {
Fp::from(self).compute_naf()
}
}
/// the code below makes the base field "visible" to higher
/// order extensions. The issue is really the fact that generic
Expand All @@ -510,9 +547,6 @@ impl Fp {
/// by manually specifying the traits D, N. This enforces the logic
/// by means of manual input.
impl FieldExtensionTrait<2, 2> for Fp {
fn quadratic_non_residue() -> Self {
<Fp as FieldExtensionTrait<1, 1>>::quadratic_non_residue()
}
fn rand<R: CryptoRngCore>(rng: &mut R) -> Self {
<Fp as FieldExtensionTrait<1, 1>>::rand(rng)
}
Expand Down
31 changes: 12 additions & 19 deletions src/fields/fp12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,27 +162,11 @@ const FROBENIUS_COEFF_FP12_C1: &[Fp2; 12] = &[
])),
]),
];
const FP12_QUADRATIC_NON_RESIDUE: Fp12 = Fp12::new(&[
Fp6::new(&[
Fp2::new(&[Fp::ZERO, Fp::ZERO]),
Fp2::new(&[Fp::ZERO, Fp::ZERO]),
Fp2::new(&[Fp::ZERO, Fp::ZERO]),
]),
Fp6::new(&[
Fp2::new(&[Fp::ONE, Fp::ZERO]),
Fp2::new(&[Fp::ZERO, Fp::ZERO]),
Fp2::new(&[Fp::ZERO, Fp::ZERO]),
]),
]);

/// type alias for dodectic extension in the representation a + bw for a,b\in Fp6
pub type Fp12 = FieldExtension<12, 2, Fp6>;

impl FieldExtensionTrait<12, 2> for Fp12 {
fn quadratic_non_residue() -> Self {
// Self::new(&[Fp6::zero(), Fp6::one()])
FP12_QUADRATIC_NON_RESIDUE
}
fn rand<R: CryptoRngCore>(rng: &mut R) -> Self {
Self([
<Fp6 as FieldExtensionTrait<6, 3>>::rand(rng),
Expand All @@ -196,10 +180,10 @@ impl FieldExtensionTrait<12, 2> for Fp12 {

impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 {
type Output = Fp12;
#[inline]
fn mul(self, other: &'b Fp12) -> Self::Output {
// this is again simple Karatsuba multiplication
// see comments in Fp2 impl of `Mul` trait, or otherwise see Alg 20 of
// <https://eprint.iacr.org/2010/354.pdf>
// this is simple FOIL'ing of the multiplication of the Fp12 elements in their (Fp6, Fp6)
// representation, see Alg 20 of <https://eprint.iacr.org/2010/354.pdf>
let t0 = self.0[0] * other.0[0];
let t1 = self.0[1] * other.0[1];
tracing::debug!(?t0, ?t1, "Fp12::mul");
Expand All @@ -212,17 +196,20 @@ impl<'a, 'b> Mul<&'b Fp12> for &'a Fp12 {
}
impl Mul for Fp12 {
type Output = Self;
#[inline]
fn mul(self, other: Self) -> Self::Output {
(&self).mul(&other)
}
}
impl MulAssign for Fp12 {
#[inline]
fn mul_assign(&mut self, other: Self) {
*self = *self * other;
}
}
impl Inv for Fp12 {
type Output = Self;
#[inline]
fn inv(self) -> Self::Output {
// Implements Alg 23 of <https://eprint.iacr.org/2010/354.pdf>
let tmp = (self.0[0].square() - (self.0[1].square().residue_mul())).inv();
Expand All @@ -232,6 +219,7 @@ impl Inv for Fp12 {
}

impl One for Fp12 {
#[inline]
fn one() -> Self {
Self::new(&[Fp6::one(), Fp6::zero()])
}
Expand All @@ -243,11 +231,13 @@ impl One for Fp12 {
#[allow(clippy::suspicious_arithmetic_impl)]
impl Div for Fp12 {
type Output = Self;
#[inline]
fn div(self, other: Self) -> Self::Output {
self * other.inv()
}
}
impl DivAssign for Fp12 {
#[inline]
fn div_assign(&mut self, other: Self) {
*self = *self / other;
}
Expand All @@ -264,6 +254,7 @@ impl ConditionallySelectable for Fp12 {
}
/// Below are additional functions needed on Fp12 for the pairing operations
impl Fp12 {
#[inline]
pub(crate) fn unitary_inverse(&self) -> Self {
Self::new(&[self.0[0], -self.0[1]])
}
Expand Down Expand Up @@ -378,6 +369,7 @@ impl Fp12 {

Fp12::new(&[Fp6::new(&[z0, z1, z2]), Fp6::new(&[z3, z4, z5])])
}
#[inline(always)]
pub(crate) fn frobenius(&self, exponent: usize) -> Self {
Self::new(&[
self.0[0].frobenius(exponent),
Expand All @@ -386,6 +378,7 @@ impl Fp12 {
.scale(FROBENIUS_COEFF_FP12_C1[exponent % 12]),
])
}
#[inline]
pub(crate) fn square(&self) -> Self {
// For F_{p^{12}} = F_{p^6}(w)/(w^2-\gamma), and A=a_0 + a_1*w \in F_{p^{12}},
// we determine C=c_0+c_1*w = A^2\in F_{p^{12}}
Expand Down
Loading
Loading