diff --git a/Cargo.lock b/Cargo.lock index 3697583dfc..141d87dd3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2759,6 +2759,7 @@ dependencies = [ name = "snarkvm-circuit-types-field" version = "0.16.19" dependencies = [ + "criterion", "snarkvm-circuit-environment", "snarkvm-circuit-types-boolean", "snarkvm-console-types-field", diff --git a/circuit/algorithms/src/poseidon/hash_many.rs b/circuit/algorithms/src/poseidon/hash_many.rs index d2baf7fcc5..2db484121a 100644 --- a/circuit/algorithms/src/poseidon/hash_many.rs +++ b/circuit/algorithms/src/poseidon/hash_many.rs @@ -163,8 +163,7 @@ impl Poseidon { /// Apply the Maximally Distance Separating (MDS) matrix in-place. #[inline] - fn apply_mds(&self, state: &mut [Field]) { - let mut new_state = Vec::with_capacity(state.len()); + fn apply_mds(&self, state: &mut [Field], new_state: &mut Vec>) { for i in 0..state.len() { let mut accumulator = Field::zero(); for (j, element) in state.iter().enumerate() { @@ -172,7 +171,7 @@ impl Poseidon { } new_state.push(accumulator); } - state.clone_from_slice(&new_state); + state.swap_with_slice(new_state); } /// Apply the permutation for all rounds in-place. @@ -183,11 +182,13 @@ impl Poseidon { let partial_round_range = full_rounds_over_2..(full_rounds_over_2 + self.partial_rounds); // Iterate through all rounds to permute. + let mut new_state = Vec::with_capacity(state.len()); for i in 0..(self.partial_rounds + self.full_rounds) { let is_full_round = !partial_round_range.contains(&i); self.apply_ark(state, i); self.apply_s_box(state, is_full_round); - self.apply_mds(state); + self.apply_mds(state, &mut new_state); + new_state.clear(); } } } diff --git a/circuit/environment/src/helpers/linear_combination.rs b/circuit/environment/src/helpers/linear_combination.rs index 4d33d210ff..85646473da 100644 --- a/circuit/environment/src/helpers/linear_combination.rs +++ b/circuit/environment/src/helpers/linear_combination.rs @@ -17,7 +17,7 @@ use snarkvm_fields::PrimeField; use core::{ fmt, - ops::{Add, AddAssign, Mul, Neg, Sub}, + ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub}, }; // Before high level program operations are converted into constraints, they are first tracked as linear combinations. @@ -463,6 +463,20 @@ impl Mul<&F> for &LinearCombination { } } +impl MulAssign<&F> for LinearCombination { + fn mul_assign(&mut self, coefficient: &F) { + self.constant *= coefficient; + self.terms = std::mem::take(&mut self.terms) + .into_iter() + .filter_map(|(v, current_coefficient)| { + let res = current_coefficient * coefficient; + (!res.is_zero()).then_some((v, res)) + }) + .collect(); + self.value *= coefficient; + } +} + impl fmt::Debug for LinearCombination { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { let mut output = format!("Constant({})", self.constant); diff --git a/circuit/types/field/Cargo.toml b/circuit/types/field/Cargo.toml index acc1367886..918186a710 100644 --- a/circuit/types/field/Cargo.toml +++ b/circuit/types/field/Cargo.toml @@ -6,6 +6,11 @@ description = "Field circuit for a decentralized virtual machine" license = "Apache-2.0" edition = "2021" +[[bench]] +name = "mul" +path = "benches/mul.rs" +harness = false + [dependencies.console] package = "snarkvm-console-types-field" path = "../../../console/types/field" @@ -20,6 +25,9 @@ version = "=0.16.19" path = "../boolean" version = "=0.16.19" +[dev-dependencies.criterion] +version = "0.5" + [features] default = [ "enable_console" ] enable_console = [ "console" ] diff --git a/circuit/types/field/benches/mul.rs b/circuit/types/field/benches/mul.rs new file mode 100644 index 0000000000..ba93c66267 --- /dev/null +++ b/circuit/types/field/benches/mul.rs @@ -0,0 +1,41 @@ +// 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. + +#[macro_use] +extern crate criterion; + +use snarkvm_circuit_environment::*; +use snarkvm_circuit_types_field::Field; + +use criterion::Criterion; + +fn bench_mul(c: &mut Criterion) { + c.bench_function("Field::mul_assign", move |b| { + let one = Field::::one(); + let two = one.clone() + &one; + let mut base = two.clone(); + + b.iter(|| { + base *= &two; + }) + }); +} + +criterion_group! { + name = mul; + config = Criterion::default(); + targets = bench_mul +} + +criterion_main!(mul); diff --git a/circuit/types/field/src/mul.rs b/circuit/types/field/src/mul.rs index 8a0e9baba8..9b5f78569c 100644 --- a/circuit/types/field/src/mul.rs +++ b/circuit/types/field/src/mul.rs @@ -59,7 +59,7 @@ impl MulAssign> for Field { impl MulAssign<&Field> for Field { fn mul_assign(&mut self, other: &Field) { match (self.is_constant(), other.is_constant()) { - (true, true) | (false, true) => *self = (&self.linear_combination * *other.eject_value()).into(), + (true, true) | (false, true) => self.linear_combination *= &*other.eject_value(), (true, false) => *self = (&other.linear_combination * *self.eject_value()).into(), (false, false) => { let product = witness!(|self, other| self * other);