Skip to content

Commit

Permalink
chore: reduce number of iterations of acvm::compiler::compile (#7050)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomAFrench authored Jan 14, 2025
1 parent 36f9751 commit 31dbfe3
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 41 deletions.
41 changes: 3 additions & 38 deletions acvm-repo/acvm/src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ pub use simulator::CircuitSimulator;
use transformers::transform_internal;
pub use transformers::{transform, MIN_EXPRESSION_WIDTH};

/// We need multiple passes to stabilize the output.
/// The value was determined by running tests.
const MAX_OPTIMIZER_PASSES: usize = 3;

/// This module moves and decomposes acir opcodes. The transformation map allows consumers of this module to map
/// metadata they had about the opcodes to the new opcode structure generated after the transformation.
#[derive(Debug)]
Expand Down Expand Up @@ -84,41 +80,10 @@ pub fn compile<F: AcirField>(
) -> (Circuit<F>, AcirTransformationMap) {
let acir_opcode_positions = (0..acir.opcodes.len()).collect::<Vec<_>>();

if MAX_OPTIMIZER_PASSES == 0 {
return (acir, AcirTransformationMap::new(&acir_opcode_positions));
}

let mut pass = 0;
let mut prev_opcodes_hash = fxhash::hash64(&acir.opcodes);
let mut prev_acir = acir;
let mut prev_acir_opcode_positions = acir_opcode_positions;

// For most test programs it would be enough to only loop `transform_internal`,
// but some of them don't stabilize unless we also repeat the backend agnostic optimizations.
let (mut acir, acir_opcode_positions) = loop {
let (acir, acir_opcode_positions) =
optimize_internal(prev_acir, prev_acir_opcode_positions);

// Stop if we have already done at least one transform and an extra optimization changed nothing.
if pass > 0 && prev_opcodes_hash == fxhash::hash64(&acir.opcodes) {
break (acir, acir_opcode_positions);
}

let (acir, acir_opcode_positions) =
transform_internal(acir, expression_width, acir_opcode_positions);

let opcodes_hash = fxhash::hash64(&acir.opcodes);

// Stop if the output hasn't change in this loop or we went too long.
if pass == MAX_OPTIMIZER_PASSES - 1 || prev_opcodes_hash == opcodes_hash {
break (acir, acir_opcode_positions);
}
let (acir, acir_opcode_positions) = optimize_internal(acir, acir_opcode_positions);

pass += 1;
prev_acir = acir;
prev_opcodes_hash = opcodes_hash;
prev_acir_opcode_positions = acir_opcode_positions;
};
let (mut acir, acir_opcode_positions) =
transform_internal(acir, expression_width, acir_opcode_positions);

let transformation_map = AcirTransformationMap::new(&acir_opcode_positions);
acir.assert_messages = transform_assert_messages(acir.assert_messages, &transformation_map);
Expand Down
23 changes: 20 additions & 3 deletions acvm-repo/acvm/src/compiler/transformers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ mod csat;

pub(crate) use csat::CSatTransformer;
pub use csat::MIN_EXPRESSION_WIDTH;
use tracing::info;

use super::{
optimizers::MergeExpressionsOptimizer, transform_assert_messages, AcirTransformationMap,
MAX_OPTIMIZER_PASSES,
optimizers::{MergeExpressionsOptimizer, RangeOptimizer},
transform_assert_messages, AcirTransformationMap,
};

/// We need multiple passes to stabilize the output.
/// The value was determined by running tests.
const MAX_TRANSFORMER_PASSES: usize = 3;

/// Applies [`ProofSystemCompiler`][crate::ProofSystemCompiler] specific optimizations to a [`Circuit`].
pub fn transform<F: AcirField>(
acir: Circuit<F>,
Expand Down Expand Up @@ -50,12 +55,18 @@ pub(super) fn transform_internal<F: AcirField>(
expression_width: ExpressionWidth,
mut acir_opcode_positions: Vec<usize>,
) -> (Circuit<F>, Vec<usize>) {
if acir.opcodes.len() == 1 && matches!(acir.opcodes[0], Opcode::BrilligCall { .. }) {
info!("Program is fully unconstrained, skipping transformation pass");
return (acir, acir_opcode_positions);
}

// Allow multiple passes until we have stable output.
let mut prev_opcodes_hash = fxhash::hash64(&acir.opcodes);

// For most test programs it would be enough to loop here, but some of them
// don't stabilize unless we also repeat the backend agnostic optimizations.
for _ in 0..MAX_OPTIMIZER_PASSES {
for _ in 0..MAX_TRANSFORMER_PASSES {
info!("Number of opcodes {}", acir.opcodes.len());
let (new_acir, new_acir_opcode_positions) =
transform_internal_once(acir, expression_width, acir_opcode_positions);

Expand Down Expand Up @@ -217,6 +228,12 @@ fn transform_internal_once<F: AcirField>(
..acir
};

// The `MergeOptimizer` can merge two witnesses which have range opcodes applied to them
// so we run the `RangeOptimizer` afterwards to clear these up.
let range_optimizer = RangeOptimizer::new(acir);
let (acir, new_acir_opcode_positions) =
range_optimizer.replace_redundant_ranges(new_acir_opcode_positions);

(acir, new_acir_opcode_positions)
}

Expand Down

0 comments on commit 31dbfe3

Please sign in to comment.