Skip to content

Commit 1cad7c8

Browse files
vezenovmTomAFrench
andauthored
feat(profiler): Reduce memory in Brillig execution flamegraph (#6538)
Co-authored-by: Tom French <15848336+TomAFrench@users.noreply.github.com>
1 parent 2f823a7 commit 1cad7c8

6 files changed

+110
-72
lines changed

tooling/profiler/Cargo.toml

-3
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,3 @@ noirc_abi.workspace = true
4444
noirc_driver.workspace = true
4545
tempfile.workspace = true
4646

47-
[features]
48-
default = ["bn254"]
49-
bn254 = ["acir/bn254"]

tooling/profiler/src/cli/execution_flamegraph_cmd.rs

+11-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
use std::path::{Path, PathBuf};
22

33
use acir::circuit::OpcodeLocation;
4-
use acir::FieldElement;
54
use clap::Args;
65
use color_eyre::eyre::{self, Context};
76

8-
use crate::flamegraph::{FlamegraphGenerator, InfernoFlamegraphGenerator, Sample};
7+
use crate::flamegraph::{BrilligExecutionSample, FlamegraphGenerator, InfernoFlamegraphGenerator};
98
use crate::fs::{read_inputs_from_file, read_program_from_file};
10-
use crate::opcode_formatter::AcirOrBrilligOpcode;
9+
use crate::opcode_formatter::format_brillig_opcode;
1110
use bn254_blackbox_solver::Bn254BlackBoxSolver;
1211
use nargo::ops::DefaultForeignCallExecutor;
1312
use noirc_abi::input_parser::Format;
@@ -51,19 +50,21 @@ fn run_with_generator(
5150
let initial_witness = program.abi.encode(&inputs_map, None)?;
5251

5352
println!("Executing");
54-
let (_, profiling_samples) = nargo::ops::execute_program_with_profiling(
53+
let (_, mut profiling_samples) = nargo::ops::execute_program_with_profiling(
5554
&program.bytecode,
5655
initial_witness,
5756
&Bn254BlackBoxSolver,
5857
&mut DefaultForeignCallExecutor::new(true, None, None, None),
5958
)?;
6059
println!("Executed");
6160

62-
let profiling_samples: Vec<Sample<FieldElement>> = profiling_samples
63-
.into_iter()
61+
println!("Collecting {} samples", profiling_samples.len());
62+
63+
let profiling_samples: Vec<BrilligExecutionSample> = profiling_samples
64+
.iter_mut()
6465
.map(|sample| {
65-
let call_stack = sample.call_stack;
66-
let brillig_function_id = sample.brillig_function_id;
66+
let call_stack = std::mem::take(&mut sample.call_stack);
67+
let brillig_function_id = std::mem::take(&mut sample.brillig_function_id);
6768
let last_entry = call_stack.last();
6869
let opcode = brillig_function_id
6970
.and_then(|id| program.bytecode.unconstrained_functions.get(id.0 as usize))
@@ -74,8 +75,8 @@ fn run_with_generator(
7475
None
7576
}
7677
})
77-
.map(|opcode| AcirOrBrilligOpcode::Brillig(opcode.clone()));
78-
Sample { opcode, call_stack, count: 1, brillig_function_id }
78+
.map(format_brillig_opcode);
79+
BrilligExecutionSample { opcode, call_stack, brillig_function_id }
7980
})
8081
.collect();
8182

tooling/profiler/src/cli/gates_flamegraph_cmd.rs

+7-10
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use color_eyre::eyre::{self, Context};
66

77
use noirc_artifacts::debug::DebugArtifact;
88

9-
use crate::flamegraph::{FlamegraphGenerator, InfernoFlamegraphGenerator, Sample};
9+
use crate::flamegraph::{CompilationSample, FlamegraphGenerator, InfernoFlamegraphGenerator};
1010
use crate::fs::read_program_from_file;
1111
use crate::gates_provider::{BackendGatesProvider, GatesProvider};
12-
use crate::opcode_formatter::AcirOrBrilligOpcode;
12+
use crate::opcode_formatter::format_acir_opcode;
1313

1414
#[derive(Debug, Clone, Args)]
1515
pub(crate) struct GatesFlamegraphCommand {
@@ -83,8 +83,8 @@ fn run_with_provider<Provider: GatesProvider, Generator: FlamegraphGenerator>(
8383
.into_iter()
8484
.zip(bytecode.opcodes)
8585
.enumerate()
86-
.map(|(index, (gates, opcode))| Sample {
87-
opcode: Some(AcirOrBrilligOpcode::Acir(opcode)),
86+
.map(|(index, (gates, opcode))| CompilationSample {
87+
opcode: Some(format_acir_opcode(&opcode)),
8888
call_stack: vec![OpcodeLocation::Acir(index)],
8989
count: gates,
9090
brillig_function_id: None,
@@ -106,10 +106,7 @@ fn run_with_provider<Provider: GatesProvider, Generator: FlamegraphGenerator>(
106106

107107
#[cfg(test)]
108108
mod tests {
109-
use acir::{
110-
circuit::{Circuit, Program},
111-
AcirField,
112-
};
109+
use acir::circuit::{Circuit, Program};
113110
use color_eyre::eyre::{self};
114111
use fm::codespan_files::Files;
115112
use noirc_artifacts::program::ProgramArtifact;
@@ -143,9 +140,9 @@ mod tests {
143140
struct TestFlamegraphGenerator {}
144141

145142
impl super::FlamegraphGenerator for TestFlamegraphGenerator {
146-
fn generate_flamegraph<'files, F: AcirField>(
143+
fn generate_flamegraph<'files, S: Sample>(
147144
&self,
148-
_samples: Vec<Sample<F>>,
145+
_samples: Vec<S>,
149146
_debug_symbols: &DebugInfo,
150147
_files: &'files impl Files<'files, FileId = fm::FileId>,
151148
_artifact_name: &str,

tooling/profiler/src/cli/opcodes_flamegraph_cmd.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ use color_eyre::eyre::{self, Context};
77

88
use noirc_artifacts::debug::DebugArtifact;
99

10-
use crate::flamegraph::{FlamegraphGenerator, InfernoFlamegraphGenerator, Sample};
10+
use crate::flamegraph::{CompilationSample, FlamegraphGenerator, InfernoFlamegraphGenerator};
1111
use crate::fs::read_program_from_file;
12-
use crate::opcode_formatter::AcirOrBrilligOpcode;
12+
use crate::opcode_formatter::{format_acir_opcode, format_brillig_opcode};
1313

1414
#[derive(Debug, Clone, Args)]
1515
pub(crate) struct OpcodesFlamegraphCommand {
@@ -59,8 +59,8 @@ fn run_with_generator<Generator: FlamegraphGenerator>(
5959
.opcodes
6060
.iter()
6161
.enumerate()
62-
.map(|(index, opcode)| Sample {
63-
opcode: Some(AcirOrBrilligOpcode::Acir(opcode.clone())),
62+
.map(|(index, opcode)| CompilationSample {
63+
opcode: Some(format_acir_opcode(opcode)),
6464
call_stack: vec![OpcodeLocation::Acir(index)],
6565
count: 1,
6666
brillig_function_id: None,
@@ -96,8 +96,8 @@ fn run_with_generator<Generator: FlamegraphGenerator>(
9696
.bytecode
9797
.into_iter()
9898
.enumerate()
99-
.map(|(brillig_index, opcode)| Sample {
100-
opcode: Some(AcirOrBrilligOpcode::Brillig(opcode)),
99+
.map(|(brillig_index, opcode)| CompilationSample {
100+
opcode: Some(format_brillig_opcode(&opcode)),
101101
call_stack: vec![OpcodeLocation::Brillig {
102102
acir_index: acir_opcode_index,
103103
brillig_index,
@@ -146,7 +146,7 @@ mod tests {
146146
brillig::{BrilligBytecode, BrilligFunctionId},
147147
Circuit, Opcode, Program,
148148
},
149-
AcirField, FieldElement,
149+
FieldElement,
150150
};
151151
use color_eyre::eyre::{self};
152152
use fm::codespan_files::Files;
@@ -160,9 +160,9 @@ mod tests {
160160
struct TestFlamegraphGenerator {}
161161

162162
impl super::FlamegraphGenerator for TestFlamegraphGenerator {
163-
fn generate_flamegraph<'files, F: AcirField>(
163+
fn generate_flamegraph<'files, S: Sample>(
164164
&self,
165-
_samples: Vec<Sample<F>>,
165+
_samples: Vec<S>,
166166
_debug_symbols: &DebugInfo,
167167
_files: &'files impl Files<'files, FileId = fm::FileId>,
168168
_artifact_name: &str,

tooling/profiler/src/flamegraph.rs

+77-27
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::{collections::BTreeMap, io::BufWriter};
33

44
use acir::circuit::brillig::BrilligFunctionId;
55
use acir::circuit::OpcodeLocation;
6-
use acir::AcirField;
76
use color_eyre::eyre::{self};
87
use fm::codespan_files::Files;
98
use fxhash::FxHashMap as HashMap;
@@ -13,18 +12,66 @@ use noirc_errors::reporter::line_and_column_from_span;
1312
use noirc_errors::Location;
1413
use noirc_evaluator::brillig::ProcedureId;
1514

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

18-
use super::opcode_formatter::format_opcode;
18+
fn brillig_function_id(&self) -> Option<BrilligFunctionId>;
19+
20+
fn call_stack(&self) -> &[OpcodeLocation];
21+
22+
fn opcode(self) -> Option<String>;
23+
}
1924

2025
#[derive(Debug)]
21-
pub(crate) struct Sample<F: AcirField> {
22-
pub(crate) opcode: Option<AcirOrBrilligOpcode<F>>,
26+
pub(crate) struct CompilationSample {
27+
pub(crate) opcode: Option<String>,
2328
pub(crate) call_stack: Vec<OpcodeLocation>,
2429
pub(crate) count: usize,
2530
pub(crate) brillig_function_id: Option<BrilligFunctionId>,
2631
}
2732

33+
impl Sample for CompilationSample {
34+
fn count(&self) -> usize {
35+
self.count
36+
}
37+
38+
fn brillig_function_id(&self) -> Option<BrilligFunctionId> {
39+
self.brillig_function_id
40+
}
41+
42+
fn call_stack(&self) -> &[OpcodeLocation] {
43+
&self.call_stack
44+
}
45+
46+
fn opcode(self) -> Option<String> {
47+
self.opcode
48+
}
49+
}
50+
51+
pub(crate) struct BrilligExecutionSample {
52+
pub(crate) opcode: Option<String>,
53+
pub(crate) call_stack: Vec<OpcodeLocation>,
54+
pub(crate) brillig_function_id: Option<BrilligFunctionId>,
55+
}
56+
57+
impl Sample for BrilligExecutionSample {
58+
fn count(&self) -> usize {
59+
1
60+
}
61+
62+
fn brillig_function_id(&self) -> Option<BrilligFunctionId> {
63+
self.brillig_function_id
64+
}
65+
66+
fn call_stack(&self) -> &[OpcodeLocation] {
67+
&self.call_stack
68+
}
69+
70+
fn opcode(self) -> Option<String> {
71+
self.opcode
72+
}
73+
}
74+
2875
#[derive(Debug, Default)]
2976
pub(crate) struct FoldedStackItem {
3077
pub(crate) total_samples: usize,
@@ -33,9 +80,9 @@ pub(crate) struct FoldedStackItem {
3380

3481
pub(crate) trait FlamegraphGenerator {
3582
#[allow(clippy::too_many_arguments)]
36-
fn generate_flamegraph<'files, F: AcirField>(
83+
fn generate_flamegraph<'files, S: Sample>(
3784
&self,
38-
samples: Vec<Sample<F>>,
85+
samples: Vec<S>,
3986
debug_symbols: &DebugInfo,
4087
files: &'files impl Files<'files, FileId = fm::FileId>,
4188
artifact_name: &str,
@@ -49,9 +96,9 @@ pub(crate) struct InfernoFlamegraphGenerator {
4996
}
5097

5198
impl FlamegraphGenerator for InfernoFlamegraphGenerator {
52-
fn generate_flamegraph<'files, F: AcirField>(
99+
fn generate_flamegraph<'files, S: Sample>(
53100
&self,
54-
samples: Vec<Sample<F>>,
101+
samples: Vec<S>,
55102
debug_symbols: &DebugInfo,
56103
files: &'files impl Files<'files, FileId = fm::FileId>,
57104
artifact_name: &str,
@@ -82,8 +129,8 @@ impl FlamegraphGenerator for InfernoFlamegraphGenerator {
82129
}
83130
}
84131

85-
fn generate_folded_sorted_lines<'files, F: AcirField>(
86-
samples: Vec<Sample<F>>,
132+
fn generate_folded_sorted_lines<'files, S: Sample>(
133+
samples: Vec<S>,
87134
debug_symbols: &DebugInfo,
88135
files: &'files impl Files<'files, FileId = fm::FileId>,
89136
) -> Vec<String> {
@@ -92,15 +139,15 @@ fn generate_folded_sorted_lines<'files, F: AcirField>(
92139

93140
let mut resolution_cache: HashMap<OpcodeLocation, Vec<String>> = HashMap::default();
94141
for sample in samples {
95-
let mut location_names = Vec::with_capacity(sample.call_stack.len());
96-
for opcode_location in sample.call_stack {
142+
let mut location_names = Vec::with_capacity(sample.call_stack().len());
143+
for opcode_location in sample.call_stack() {
97144
let callsite_labels = resolution_cache
98-
.entry(opcode_location)
145+
.entry(*opcode_location)
99146
.or_insert_with(|| {
100147
find_callsite_labels(
101148
debug_symbols,
102-
&opcode_location,
103-
sample.brillig_function_id,
149+
opcode_location,
150+
sample.brillig_function_id(),
104151
files,
105152
)
106153
})
@@ -109,11 +156,14 @@ fn generate_folded_sorted_lines<'files, F: AcirField>(
109156
location_names.extend(callsite_labels);
110157
}
111158

112-
if let Some(opcode) = &sample.opcode {
113-
location_names.push(format_opcode(opcode));
159+
// We move `sample` by calling `sample.opcode()` so we want to fetch the sample count here.
160+
let count = sample.count();
161+
162+
if let Some(opcode) = sample.opcode() {
163+
location_names.push(opcode);
114164
}
115165

116-
add_locations_to_folded_stack_items(&mut folded_stack_items, location_names, sample.count);
166+
add_locations_to_folded_stack_items(&mut folded_stack_items, location_names, count);
117167
}
118168

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

254-
use crate::{flamegraph::Sample, opcode_formatter::AcirOrBrilligOpcode};
304+
use crate::{flamegraph::CompilationSample, opcode_formatter::format_acir_opcode};
255305

256306
use super::generate_folded_sorted_lines;
257307

@@ -338,25 +388,25 @@ mod tests {
338388
BTreeMap::default(),
339389
);
340390

341-
let samples: Vec<Sample<FieldElement>> = vec![
342-
Sample {
343-
opcode: Some(AcirOrBrilligOpcode::Acir(AcirOpcode::AssertZero(
391+
let samples: Vec<CompilationSample> = vec![
392+
CompilationSample {
393+
opcode: Some(format_acir_opcode(&AcirOpcode::AssertZero::<FieldElement>(
344394
Expression::default(),
345395
))),
346396
call_stack: vec![OpcodeLocation::Acir(0)],
347397
count: 10,
348398
brillig_function_id: None,
349399
},
350-
Sample {
351-
opcode: Some(AcirOrBrilligOpcode::Acir(AcirOpcode::AssertZero(
400+
CompilationSample {
401+
opcode: Some(format_acir_opcode(&AcirOpcode::AssertZero::<FieldElement>(
352402
Expression::default(),
353403
))),
354404
call_stack: vec![OpcodeLocation::Acir(1)],
355405
count: 20,
356406
brillig_function_id: None,
357407
},
358-
Sample {
359-
opcode: Some(AcirOrBrilligOpcode::Acir(AcirOpcode::MemoryInit {
408+
CompilationSample {
409+
opcode: Some(format_acir_opcode(&AcirOpcode::MemoryInit::<FieldElement> {
360410
block_id: BlockId(0),
361411
init: vec![],
362412
block_type: acir::circuit::opcodes::BlockType::Memory,

tooling/profiler/src/opcode_formatter.rs

+6-13
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,6 @@ use acir::brillig::{BinaryFieldOp, BinaryIntOp, BlackBoxOp, Opcode as BrilligOpc
22
use acir::circuit::{directives::Directive, opcodes::BlackBoxFuncCall, Opcode as AcirOpcode};
33
use acir::AcirField;
44

5-
#[derive(Debug)]
6-
pub(crate) enum AcirOrBrilligOpcode<F: AcirField> {
7-
Acir(AcirOpcode<F>),
8-
Brillig(BrilligOpcode<F>),
9-
}
10-
115
fn format_blackbox_function<F>(call: &BlackBoxFuncCall<F>) -> String {
126
match call {
137
BlackBoxFuncCall::AES128Encrypt { .. } => "aes128_encrypt".to_string(),
@@ -136,11 +130,10 @@ fn format_brillig_opcode_kind<F>(opcode: &BrilligOpcode<F>) -> String {
136130
}
137131
}
138132

139-
pub(crate) fn format_opcode<F: AcirField>(opcode: &AcirOrBrilligOpcode<F>) -> String {
140-
match opcode {
141-
AcirOrBrilligOpcode::Acir(opcode) => format!("acir::{}", format_acir_opcode_kind(opcode)),
142-
AcirOrBrilligOpcode::Brillig(opcode) => {
143-
format!("brillig::{}", format_brillig_opcode_kind(opcode))
144-
}
145-
}
133+
pub(crate) fn format_acir_opcode<F: AcirField>(opcode: &AcirOpcode<F>) -> String {
134+
format!("acir::{}", format_acir_opcode_kind(opcode))
135+
}
136+
137+
pub(crate) fn format_brillig_opcode<F: AcirField>(opcode: &BrilligOpcode<F>) -> String {
138+
format!("brillig::{}", format_brillig_opcode_kind(opcode))
146139
}

0 commit comments

Comments
 (0)