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

feat(profiler): Reduce memory in Brillig execution flamegraph #6538

Merged
merged 13 commits into from
Nov 18, 2024
3 changes: 0 additions & 3 deletions tooling/profiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,3 @@ noirc_abi.workspace = true
noirc_driver.workspace = true
tempfile.workspace = true

[features]
default = ["bn254"]
bn254 = ["acir/bn254"]
21 changes: 11 additions & 10 deletions tooling/profiler/src/cli/execution_flamegraph_cmd.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use std::path::{Path, PathBuf};

use acir::circuit::OpcodeLocation;
use acir::FieldElement;
use clap::Args;
use color_eyre::eyre::{self, Context};

use crate::flamegraph::{FlamegraphGenerator, InfernoFlamegraphGenerator, Sample};
use crate::flamegraph::{BrilligExecutionSample, FlamegraphGenerator, InfernoFlamegraphGenerator};
use crate::fs::{read_inputs_from_file, read_program_from_file};
use crate::opcode_formatter::AcirOrBrilligOpcode;
use crate::opcode_formatter::format_brillig_opcode;
use bn254_blackbox_solver::Bn254BlackBoxSolver;
use nargo::ops::DefaultForeignCallExecutor;
use noirc_abi::input_parser::Format;
Expand Down Expand Up @@ -51,19 +50,21 @@ fn run_with_generator(
let initial_witness = program.abi.encode(&inputs_map, None)?;

println!("Executing");
let (_, profiling_samples) = nargo::ops::execute_program_with_profiling(
let (_, mut profiling_samples) = nargo::ops::execute_program_with_profiling(
&program.bytecode,
initial_witness,
&Bn254BlackBoxSolver,
&mut DefaultForeignCallExecutor::new(true, None, None, None),
)?;
println!("Executed");

let profiling_samples: Vec<Sample<FieldElement>> = profiling_samples
.into_iter()
println!("Collecting {} samples", profiling_samples.len());

let profiling_samples: Vec<BrilligExecutionSample> = profiling_samples
.iter_mut()
.map(|sample| {
let call_stack = sample.call_stack;
let brillig_function_id = sample.brillig_function_id;
let call_stack = std::mem::take(&mut sample.call_stack);
let brillig_function_id = std::mem::take(&mut sample.brillig_function_id);
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
let last_entry = call_stack.last();
let opcode = brillig_function_id
.and_then(|id| program.bytecode.unconstrained_functions.get(id.0 as usize))
Expand All @@ -74,8 +75,8 @@ fn run_with_generator(
None
}
})
.map(|opcode| AcirOrBrilligOpcode::Brillig(opcode.clone()));
Sample { opcode, call_stack, count: 1, brillig_function_id }
.map(format_brillig_opcode);
BrilligExecutionSample { opcode, call_stack, brillig_function_id }
})
.collect();

Expand Down
17 changes: 7 additions & 10 deletions tooling/profiler/src/cli/gates_flamegraph_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use color_eyre::eyre::{self, Context};

use noirc_artifacts::debug::DebugArtifact;

use crate::flamegraph::{FlamegraphGenerator, InfernoFlamegraphGenerator, Sample};
use crate::flamegraph::{CompilationSample, FlamegraphGenerator, InfernoFlamegraphGenerator};
use crate::fs::read_program_from_file;
use crate::gates_provider::{BackendGatesProvider, GatesProvider};
use crate::opcode_formatter::AcirOrBrilligOpcode;
use crate::opcode_formatter::format_acir_opcode;

#[derive(Debug, Clone, Args)]
pub(crate) struct GatesFlamegraphCommand {
Expand Down Expand Up @@ -83,8 +83,8 @@ fn run_with_provider<Provider: GatesProvider, Generator: FlamegraphGenerator>(
.into_iter()
.zip(bytecode.opcodes)
.enumerate()
.map(|(index, (gates, opcode))| Sample {
opcode: Some(AcirOrBrilligOpcode::Acir(opcode)),
.map(|(index, (gates, opcode))| CompilationSample {
opcode: Some(format_acir_opcode(&opcode)),
call_stack: vec![OpcodeLocation::Acir(index)],
count: gates,
brillig_function_id: None,
Expand All @@ -106,10 +106,7 @@ fn run_with_provider<Provider: GatesProvider, Generator: FlamegraphGenerator>(

#[cfg(test)]
mod tests {
use acir::{
circuit::{Circuit, Program},
AcirField,
};
use acir::circuit::{Circuit, Program};
use color_eyre::eyre::{self};
use fm::codespan_files::Files;
use noirc_artifacts::program::ProgramArtifact;
Expand Down Expand Up @@ -143,9 +140,9 @@ mod tests {
struct TestFlamegraphGenerator {}

impl super::FlamegraphGenerator for TestFlamegraphGenerator {
fn generate_flamegraph<'files, F: AcirField>(
fn generate_flamegraph<'files, S: Sample>(
&self,
_samples: Vec<Sample<F>>,
_samples: Vec<S>,
_debug_symbols: &DebugInfo,
_files: &'files impl Files<'files, FileId = fm::FileId>,
_artifact_name: &str,
Expand Down
18 changes: 9 additions & 9 deletions tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use color_eyre::eyre::{self, Context};

use noirc_artifacts::debug::DebugArtifact;

use crate::flamegraph::{FlamegraphGenerator, InfernoFlamegraphGenerator, Sample};
use crate::flamegraph::{CompilationSample, FlamegraphGenerator, InfernoFlamegraphGenerator};
use crate::fs::read_program_from_file;
use crate::opcode_formatter::AcirOrBrilligOpcode;
use crate::opcode_formatter::{format_acir_opcode, format_brillig_opcode};

#[derive(Debug, Clone, Args)]
pub(crate) struct OpcodesFlamegraphCommand {
Expand Down Expand Up @@ -59,8 +59,8 @@ fn run_with_generator<Generator: FlamegraphGenerator>(
.opcodes
.iter()
.enumerate()
.map(|(index, opcode)| Sample {
opcode: Some(AcirOrBrilligOpcode::Acir(opcode.clone())),
.map(|(index, opcode)| CompilationSample {
opcode: Some(format_acir_opcode(opcode)),
call_stack: vec![OpcodeLocation::Acir(index)],
count: 1,
brillig_function_id: None,
Expand Down Expand Up @@ -96,8 +96,8 @@ fn run_with_generator<Generator: FlamegraphGenerator>(
.bytecode
.into_iter()
.enumerate()
.map(|(brillig_index, opcode)| Sample {
opcode: Some(AcirOrBrilligOpcode::Brillig(opcode)),
.map(|(brillig_index, opcode)| CompilationSample {
opcode: Some(format_brillig_opcode(&opcode)),
call_stack: vec![OpcodeLocation::Brillig {
acir_index: acir_opcode_index,
brillig_index,
Expand Down Expand Up @@ -146,7 +146,7 @@ mod tests {
brillig::{BrilligBytecode, BrilligFunctionId},
Circuit, Opcode, Program,
},
AcirField, FieldElement,
FieldElement,
};
use color_eyre::eyre::{self};
use fm::codespan_files::Files;
Expand All @@ -160,9 +160,9 @@ mod tests {
struct TestFlamegraphGenerator {}

impl super::FlamegraphGenerator for TestFlamegraphGenerator {
fn generate_flamegraph<'files, F: AcirField>(
fn generate_flamegraph<'files, S: Sample>(
&self,
_samples: Vec<Sample<F>>,
_samples: Vec<S>,
_debug_symbols: &DebugInfo,
_files: &'files impl Files<'files, FileId = fm::FileId>,
_artifact_name: &str,
Expand Down
104 changes: 77 additions & 27 deletions tooling/profiler/src/flamegraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::{collections::BTreeMap, io::BufWriter};

use acir::circuit::brillig::BrilligFunctionId;
use acir::circuit::OpcodeLocation;
use acir::AcirField;
use color_eyre::eyre::{self};
use fm::codespan_files::Files;
use fxhash::FxHashMap as HashMap;
Expand All @@ -13,18 +12,66 @@ use noirc_errors::reporter::line_and_column_from_span;
use noirc_errors::Location;
use noirc_evaluator::brillig::ProcedureId;

use crate::opcode_formatter::AcirOrBrilligOpcode;
pub(crate) trait Sample {
fn count(&self) -> usize;

use super::opcode_formatter::format_opcode;
fn brillig_function_id(&self) -> Option<BrilligFunctionId>;

fn call_stack(&self) -> &[OpcodeLocation];

fn opcode(self) -> Option<String>;
}

#[derive(Debug)]
pub(crate) struct Sample<F: AcirField> {
pub(crate) opcode: Option<AcirOrBrilligOpcode<F>>,
pub(crate) struct CompilationSample {
pub(crate) opcode: Option<String>,
pub(crate) call_stack: Vec<OpcodeLocation>,
pub(crate) count: usize,
pub(crate) brillig_function_id: Option<BrilligFunctionId>,
}

impl Sample for CompilationSample {
fn count(&self) -> usize {
self.count
}

fn brillig_function_id(&self) -> Option<BrilligFunctionId> {
self.brillig_function_id
}

fn call_stack(&self) -> &[OpcodeLocation] {
&self.call_stack
}

fn opcode(self) -> Option<String> {
self.opcode
}
}

pub(crate) struct BrilligExecutionSample {
pub(crate) opcode: Option<String>,
pub(crate) call_stack: Vec<OpcodeLocation>,
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
pub(crate) brillig_function_id: Option<BrilligFunctionId>,
}

impl Sample for BrilligExecutionSample {
fn count(&self) -> usize {
1
}

fn brillig_function_id(&self) -> Option<BrilligFunctionId> {
self.brillig_function_id
}

fn call_stack(&self) -> &[OpcodeLocation] {
&self.call_stack
}

fn opcode(self) -> Option<String> {
self.opcode
}
}

#[derive(Debug, Default)]
pub(crate) struct FoldedStackItem {
pub(crate) total_samples: usize,
Expand All @@ -33,9 +80,9 @@ pub(crate) struct FoldedStackItem {

pub(crate) trait FlamegraphGenerator {
#[allow(clippy::too_many_arguments)]
fn generate_flamegraph<'files, F: AcirField>(
fn generate_flamegraph<'files, S: Sample>(
&self,
samples: Vec<Sample<F>>,
samples: Vec<S>,
debug_symbols: &DebugInfo,
files: &'files impl Files<'files, FileId = fm::FileId>,
artifact_name: &str,
Expand All @@ -49,9 +96,9 @@ pub(crate) struct InfernoFlamegraphGenerator {
}

impl FlamegraphGenerator for InfernoFlamegraphGenerator {
fn generate_flamegraph<'files, F: AcirField>(
fn generate_flamegraph<'files, S: Sample>(
&self,
samples: Vec<Sample<F>>,
samples: Vec<S>,
debug_symbols: &DebugInfo,
files: &'files impl Files<'files, FileId = fm::FileId>,
artifact_name: &str,
Expand Down Expand Up @@ -82,8 +129,8 @@ impl FlamegraphGenerator for InfernoFlamegraphGenerator {
}
}

fn generate_folded_sorted_lines<'files, F: AcirField>(
samples: Vec<Sample<F>>,
fn generate_folded_sorted_lines<'files, S: Sample>(
samples: Vec<S>,
debug_symbols: &DebugInfo,
files: &'files impl Files<'files, FileId = fm::FileId>,
) -> Vec<String> {
Expand All @@ -92,15 +139,15 @@ fn generate_folded_sorted_lines<'files, F: AcirField>(

let mut resolution_cache: HashMap<OpcodeLocation, Vec<String>> = HashMap::default();
for sample in samples {
let mut location_names = Vec::with_capacity(sample.call_stack.len());
for opcode_location in sample.call_stack {
let mut location_names = Vec::with_capacity(sample.call_stack().len());
for opcode_location in sample.call_stack() {
let callsite_labels = resolution_cache
.entry(opcode_location)
.entry(*opcode_location)
.or_insert_with(|| {
find_callsite_labels(
debug_symbols,
&opcode_location,
sample.brillig_function_id,
opcode_location,
sample.brillig_function_id(),
files,
)
})
Expand All @@ -109,11 +156,14 @@ fn generate_folded_sorted_lines<'files, F: AcirField>(
location_names.extend(callsite_labels);
}

if let Some(opcode) = &sample.opcode {
location_names.push(format_opcode(opcode));
// We move `sample` by calling `sample.opcode()` so we want to fetch the sample count here.
let count = sample.count();

if let Some(opcode) = sample.opcode() {
location_names.push(opcode);
}

add_locations_to_folded_stack_items(&mut folded_stack_items, location_names, sample.count);
add_locations_to_folded_stack_items(&mut folded_stack_items, location_names, count);
}

to_folded_sorted_lines(&folded_stack_items, Default::default())
Expand Down Expand Up @@ -251,7 +301,7 @@ mod tests {
use noirc_errors::{debug_info::DebugInfo, Location, Span};
use std::{collections::BTreeMap, path::Path};

use crate::{flamegraph::Sample, opcode_formatter::AcirOrBrilligOpcode};
use crate::{flamegraph::CompilationSample, opcode_formatter::format_acir_opcode};

use super::generate_folded_sorted_lines;

Expand Down Expand Up @@ -338,25 +388,25 @@ mod tests {
BTreeMap::default(),
);

let samples: Vec<Sample<FieldElement>> = vec![
Sample {
opcode: Some(AcirOrBrilligOpcode::Acir(AcirOpcode::AssertZero(
let samples: Vec<CompilationSample> = vec![
CompilationSample {
opcode: Some(format_acir_opcode(&AcirOpcode::AssertZero::<FieldElement>(
Expression::default(),
))),
call_stack: vec![OpcodeLocation::Acir(0)],
count: 10,
brillig_function_id: None,
},
Sample {
opcode: Some(AcirOrBrilligOpcode::Acir(AcirOpcode::AssertZero(
CompilationSample {
opcode: Some(format_acir_opcode(&AcirOpcode::AssertZero::<FieldElement>(
Expression::default(),
))),
call_stack: vec![OpcodeLocation::Acir(1)],
count: 20,
brillig_function_id: None,
},
Sample {
opcode: Some(AcirOrBrilligOpcode::Acir(AcirOpcode::MemoryInit {
CompilationSample {
opcode: Some(format_acir_opcode(&AcirOpcode::MemoryInit::<FieldElement> {
block_id: BlockId(0),
init: vec![],
block_type: acir::circuit::opcodes::BlockType::Memory,
Expand Down
19 changes: 6 additions & 13 deletions tooling/profiler/src/opcode_formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ use acir::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpc
use acir::circuit::{directives::Directive, opcodes::BlackBoxFuncCall, Opcode as AcirOpcode};
use acir::AcirField;

#[derive(Debug)]
pub(crate) enum AcirOrBrilligOpcode<F: AcirField> {
Acir(AcirOpcode<F>),
Brillig(BrilligOpcode<F>),
}

fn format_blackbox_function<F>(call: &BlackBoxFuncCall<F>) -> String {
match call {
BlackBoxFuncCall::AES128Encrypt { .. } => "aes128_encrypt".to_string(),
Expand Down Expand Up @@ -136,11 +130,10 @@ fn format_brillig_opcode_kind<F>(opcode: &BrilligOpcode<F>) -> String {
}
}

pub(crate) fn format_opcode<F: AcirField>(opcode: &AcirOrBrilligOpcode<F>) -> String {
match opcode {
AcirOrBrilligOpcode::Acir(opcode) => format!("acir::{}", format_acir_opcode_kind(opcode)),
AcirOrBrilligOpcode::Brillig(opcode) => {
format!("brillig::{}", format_brillig_opcode_kind(opcode))
}
}
pub(crate) fn format_acir_opcode<F: AcirField>(opcode: &AcirOpcode<F>) -> String {
format!("acir::{}", format_acir_opcode_kind(opcode))
}

pub(crate) fn format_brillig_opcode<F: AcirField>(opcode: &BrilligOpcode<F>) -> String {
format!("brillig::{}", format_brillig_opcode_kind(opcode))
}
Loading