Skip to content

Gather compilation statistics #1236

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 39 commits into from
Jun 2, 2025
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
9372b34
Add basic Statistics struct and its builder
JulianGCalderon May 16, 2025
8e854cf
Add Serialzie to Statistics
JulianGCalderon May 16, 2025
9bb64b7
Generate basic sierra statistics
JulianGCalderon May 16, 2025
e9246eb
Add stats argument to compile
JulianGCalderon May 16, 2025
27baa77
Save sierra to mlir and mlir passes time
JulianGCalderon May 16, 2025
5ef4dcf
Add stats argument to object_to_shared_lib
JulianGCalderon May 16, 2025
dc0f05c
Save linking time
JulianGCalderon May 16, 2025
5300e17
Add stats argument to module_to_object
JulianGCalderon May 16, 2025
6ef8644
Save llvm time
JulianGCalderon May 16, 2025
2ca97ec
Save total compilation time
JulianGCalderon May 16, 2025
0b5653e
Save object size in statistics
JulianGCalderon May 16, 2025
c375c23
Remove StatisticsBuilder
JulianGCalderon May 19, 2025
76f5b24
Add libfunc frequency
JulianGCalderon May 19, 2025
8668e78
Remove work
JulianGCalderon May 19, 2025
1e0b656
Assert frequency map is not empty
JulianGCalderon May 19, 2025
e8a1a97
Save mlir operation count
JulianGCalderon May 19, 2025
0fdf9b9
Save llvmir instructions and opcode frequency
JulianGCalderon May 19, 2025
da2e299
Save llvmir virtual register count
JulianGCalderon May 19, 2025
ba474c0
Adapt trace_dump.rs
JulianGCalderon May 19, 2025
6774ec1
Don't rely on transmute
JulianGCalderon May 19, 2025
1b8177d
Move MLIR walking logic to walk_ir module
JulianGCalderon May 19, 2025
97f4094
Move LLVMIR walking to walk_ir module
JulianGCalderon May 19, 2025
458bf97
Improve comments
JulianGCalderon May 19, 2025
29766ea
Pass statistics to compile stats
JulianGCalderon May 20, 2025
faf020f
Save MLIR operations by libfunc
JulianGCalderon May 20, 2025
24321a0
Add clone_option_mut macro
JulianGCalderon May 21, 2025
eb1a060
Validate statistics
JulianGCalderon May 21, 2025
1c516dc
Use clone_option_mut
JulianGCalderon May 21, 2025
ebbc29f
Document statistics
JulianGCalderon May 21, 2025
e7b00b4
Document walk_ir module
JulianGCalderon May 21, 2025
22f4e2c
Add API to statistics
JulianGCalderon May 22, 2025
8c7976c
Add support for saving stats on `starknet-native-compile`
JulianGCalderon May 22, 2025
e9e2483
Add documentation
JulianGCalderon May 22, 2025
2eae3f5
Fix clippy
JulianGCalderon May 22, 2025
01adb3f
Update starknet-blocks.yml ref
JulianGCalderon May 30, 2025
714eea1
Merge branch 'main' into comp-stats
JulianGCalderon May 30, 2025
dcd3cd1
Merge branch 'main' into comp-stats
gabrielbosio May 30, 2025
d6d382c
Rephrase docs
JulianGCalderon May 30, 2025
570d63d
Update script for comparing state dumps
JulianGCalderon May 30, 2025
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
16 changes: 8 additions & 8 deletions benches/compile_time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub fn bench_compile_time(c: &mut Criterion) {
b.iter(|| {
let native_context = NativeContext::new();
native_context
.compile(program, false, Some(Default::default()))
.compile(program, false, Some(Default::default()), None)
.unwrap();
// pass manager internally verifies the MLIR output is correct.
})
Expand All @@ -32,7 +32,7 @@ pub fn bench_compile_time(c: &mut Criterion) {
c.bench_with_input(BenchmarkId::new(filename, 1), &program, |b, program| {
b.iter(|| {
native_context
.compile(program, false, Some(Default::default()))
.compile(program, false, Some(Default::default()), None)
.unwrap();
// pass manager internally verifies the MLIR output is correct.
})
Expand All @@ -48,9 +48,9 @@ pub fn bench_compile_time(c: &mut Criterion) {
b.iter(|| {
let native_context = NativeContext::new();
let module = native_context
.compile(black_box(program), false, Some(Default::default()))
.compile(black_box(program), false, Some(Default::default()), None)
.unwrap();
let object = module_to_object(module.module(), OptLevel::None)
let object = module_to_object(module.module(), OptLevel::None, None)
.expect("to compile correctly to a object file");
black_box(object)
})
Expand All @@ -67,9 +67,9 @@ pub fn bench_compile_time(c: &mut Criterion) {
c.bench_with_input(BenchmarkId::new(filename, 1), &program, |b, program| {
b.iter(|| {
let module = native_context
.compile(black_box(program), false, Some(Default::default()))
.compile(black_box(program), false, Some(Default::default()), None)
.unwrap();
let object = module_to_object(module.module(), OptLevel::None)
let object = module_to_object(module.module(), OptLevel::None, None)
.expect("to compile correctly to a object file");
black_box(object)
})
Expand All @@ -86,9 +86,9 @@ pub fn bench_compile_time(c: &mut Criterion) {
c.bench_with_input(BenchmarkId::new(filename, 1), &program, |b, program| {
b.iter(|| {
let module = native_context
.compile(black_box(program), false, Some(Default::default()))
.compile(black_box(program), false, Some(Default::default()), None)
.unwrap();
let object = module_to_object(module.module(), OptLevel::Aggressive)
let object = module_to_object(module.module(), OptLevel::Aggressive, None)
.expect("to compile correctly to a object file");
black_box(object)
})
Expand Down
8 changes: 4 additions & 4 deletions benches/libfuncs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub fn bench_libfuncs(c: &mut Criterion) {
let native_context = NativeContext::new();
b.iter(|| {
let module = native_context
.compile(program, false, Some(Default::default()))
.compile(program, false, Some(Default::default()), None)
.unwrap();
// pass manager internally verifies the MLIR output is correct.
let native_executor =
Expand All @@ -77,7 +77,7 @@ pub fn bench_libfuncs(c: &mut Criterion) {
|b, program| {
let native_context = NativeContext::new();
let module = native_context
.compile(program, false, Some(Default::default()))
.compile(program, false, Some(Default::default()), None)
.unwrap();
// pass manager internally verifies the MLIR output is correct.
let native_executor =
Expand Down Expand Up @@ -108,7 +108,7 @@ pub fn bench_libfuncs(c: &mut Criterion) {
let native_context = NativeContext::new();
b.iter(|| {
let module = native_context
.compile(program, false, Some(Default::default()))
.compile(program, false, Some(Default::default()), None)
.unwrap();
// pass manager internally verifies the MLIR output is correct.
let native_executor =
Expand All @@ -130,7 +130,7 @@ pub fn bench_libfuncs(c: &mut Criterion) {
|b, program| {
let native_context = NativeContext::new();
let module = native_context
.compile(program, false, Some(Default::default()))
.compile(program, false, Some(Default::default()), None)
.unwrap();
// pass manager internally verifies the MLIR output is correct.
let native_executor =
Expand Down
2 changes: 1 addition & 1 deletion examples/easy_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() {

// Compile the sierra program into a MLIR module.
let native_program = native_context
.compile(&sierra_program, false, Some(Default::default()))
.compile(&sierra_program, false, Some(Default::default()), None)
.unwrap();

// The parameters of the entry point.
Expand Down
2 changes: 1 addition & 1 deletion examples/erc20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ fn main() {
let native_context = NativeContext::new();

let native_program = native_context
.compile(&sierra_program, false, Some(Default::default()))
.compile(&sierra_program, false, Some(Default::default()), None)
.unwrap();

let entry_point_fn =
Expand Down
2 changes: 1 addition & 1 deletion examples/invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn main() {
let native_context = NativeContext::new();

let native_program = native_context
.compile(&sierra_program, false, Some(Default::default()))
.compile(&sierra_program, false, Some(Default::default()), None)
.unwrap();

// Call the echo function from the contract using the generated wrapper.
Expand Down
2 changes: 1 addition & 1 deletion examples/starknet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ fn main() {
let native_context = NativeContext::new();

let native_program = native_context
.compile(&sierra_program, false, Some(Default::default()))
.compile(&sierra_program, false, Some(Default::default()), None)
.unwrap();

// Call the echo function from the contract using the generated wrapper.
Expand Down
7 changes: 4 additions & 3 deletions src/bin/cairo-native-compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn main() -> anyhow::Result<()> {

// Compile the sierra program into a MLIR module.
let native_module = native_context
.compile(&sierra_program, false, Some(Default::default()))
.compile(&sierra_program, false, Some(Default::default()), None)
.unwrap();

let output_mlir = args
Expand All @@ -79,9 +79,10 @@ fn main() -> anyhow::Result<()> {
})
});

let object_data = module_to_object(native_module.module(), args.opt_level.into())
let object_data = module_to_object(native_module.module(), args.opt_level.into(), None)
.context("Failed to convert module to object.")?;
object_to_shared_lib(&object_data, &output_lib).context("Failed to write shared library.")?;
object_to_shared_lib(&object_data, &output_lib, None)
.context("Failed to write shared library.")?;

Ok(())
}
2 changes: 1 addition & 1 deletion src/bin/cairo-native-dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let program = load_program(Path::new(&args.input), args.starknet)?;

// Compile the program.
let module = context.compile(&program, false, Some(Default::default()))?;
let module = context.compile(&program, false, Some(Default::default()), None)?;

// Write the output.
let output_str = module
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cairo-native-run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ fn main() -> anyhow::Result<()> {

// Compile the sierra program into a MLIR module.
let native_module = native_context
.compile(&sierra_program, false, Some(Default::default()))
.compile(&sierra_program, false, Some(Default::default()), None)
.unwrap();

let native_executor: Box<dyn Fn(_, _, _, &mut StubSyscallHandler) -> _> = match args.run_mode {
Expand Down
6 changes: 3 additions & 3 deletions src/bin/cairo-native-stress/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ where
) -> Arc<AotNativeExecutor> {
let native_module = self
.context
.compile(program, false, Some(Default::default()))
.compile(program, false, Some(Default::default()), None)
.expect("failed to compile program");

let registry = ProgramRegistry::new(program).expect("failed to get program registry");
Expand All @@ -291,15 +291,15 @@ where
.expect("module should have gas metadata");

let shared_library = {
let object_data = module_to_object(native_module.module(), opt_level)
let object_data = module_to_object(native_module.module(), opt_level, None)
.expect("failed to convert MLIR to object");

let shared_library_dir = Path::new(AOT_CACHE_DIR);
create_dir_all(shared_library_dir).expect("failed to create shared library directory");
let shared_library_name = format!("lib{key}{SHARED_LIBRARY_EXT}");
let shared_library_path = shared_library_dir.join(shared_library_name);

object_to_shared_lib(&object_data, &shared_library_path)
object_to_shared_lib(&object_data, &shared_library_path, None)
.expect("failed to link object into shared library");

unsafe {
Expand Down
1 change: 1 addition & 0 deletions src/bin/scarb-native-dump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ fn main() -> anyhow::Result<()> {
&compiled.into_v1().unwrap().program,
false,
Some(Default::default()),
None,
)
.unwrap();

Expand Down
2 changes: 1 addition & 1 deletion src/bin/utils/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ pub fn run_tests(

// Compile the sierra program into a MLIR module.
let native_module = native_context
.compile(&sierra_program, false, Some(Default::default()))
.compile(&sierra_program, false, Some(Default::default()), None)
.unwrap();

let native_executor: Box<dyn Fn(_, _, _, &mut StubSyscallHandler) -> _> = match args.run_mode {
Expand Down
6 changes: 3 additions & 3 deletions src/cache/aot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@ where
mut metadata,
} = self
.context
.compile(program, false, Some(Default::default()))?;
.compile(program, false, Some(Default::default()), None)?;

// Compile module into an object.
let object_data = crate::ffi::module_to_object(&module, opt_level)?;
let object_data = crate::ffi::module_to_object(&module, opt_level, None)?;

// Compile object into a shared library.
let shared_library_path = tempfile::Builder::new()
.prefix("lib")
.suffix(SHARED_LIBRARY_EXT)
.tempfile()?
.into_temp_path();
crate::ffi::object_to_shared_lib(&object_data, &shared_library_path)?;
crate::ffi::object_to_shared_lib(&object_data, &shared_library_path, None)?;

let shared_library = unsafe { Library::new(shared_library_path)? };
let executor = AotNativeExecutor::new(
Expand Down
2 changes: 1 addition & 1 deletion src/cache/jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ where
) -> Result<Arc<JitNativeExecutor<'a>>> {
let module = self
.context
.compile(program, false, Some(Default::default()))?;
.compile(program, false, Some(Default::default()), None)?;
let executor = JitNativeExecutor::from_native_module(module, opt_level)?;

let executor = Arc::new(executor);
Expand Down
26 changes: 12 additions & 14 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
metadata::{gas::GasMetadata, runtime_bindings::RuntimeBindingsMeta, MetadataStorage},
module::NativeModule,
native_assert,
statistics::Statistics,
utils::run_pass_manager,
};
use cairo_lang_sierra::{
Expand Down Expand Up @@ -32,7 +33,6 @@ use mlir_sys::{
MlirLLVMDINameTableKind_MlirLLVMDINameTableKindDefault,
};
use std::{sync::OnceLock, time::Instant};
use tracing::trace;

/// Context of IRs, dialects and passes for Cairo programs compilation.
#[derive(Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -69,10 +69,8 @@ impl NativeContext {
program: &Program,
ignore_debug_names: bool,
gas_metadata_config: Option<MetadataComputationConfig>,
stats: Option<&mut Statistics>,
) -> Result<NativeModule, Error> {
trace!("starting sierra to mlir compilation");
let pre_sierra_compilation_instant = Instant::now();

static INITIALIZED: OnceLock<()> = OnceLock::new();
INITIALIZED.get_or_init(|| unsafe {
LLVM_InitializeAllTargets();
Expand Down Expand Up @@ -167,6 +165,7 @@ impl NativeContext {
// Create the Sierra program registry
let registry = ProgramRegistry::<CoreType, CoreLibfunc>::new(program)?;

let pre_sierra_to_mlir_instant = Instant::now();
crate::compile(
&self.context,
&module,
Expand All @@ -176,12 +175,10 @@ impl NativeContext {
unsafe { Attribute::from_raw(di_unit_id) },
ignore_debug_names,
)?;

let sierra_compilation_time = pre_sierra_compilation_instant.elapsed().as_millis();
trace!(
time = sierra_compilation_time,
"sierra to mlir compilation finished"
);
let sierra_to_mlir_time = pre_sierra_to_mlir_instant.elapsed().as_millis();
if let Some(&mut ref mut stats) = stats {
stats.compilation_sierra_to_mlir_time_ms = Some(sierra_to_mlir_time);
}

if let Ok(x) = std::env::var("NATIVE_DEBUG_DUMP") {
if x == "1" || x == "true" {
Expand All @@ -201,11 +198,12 @@ impl NativeContext {
}
}

trace!("starting mlir passes");
let pre_passes_instant = Instant::now();
let pre_mlir_passes_instant = Instant::now();
run_pass_manager(&self.context, &mut module)?;
let passes_time = pre_passes_instant.elapsed().as_millis();
trace!(time = passes_time, "mlir passes finished");
let mlir_passes_time = pre_mlir_passes_instant.elapsed().as_millis();
if let Some(&mut ref mut stats) = stats {
stats.compilation_mlir_passes_time_ms = Some(mlir_passes_time);
}

if let Ok(x) = std::env::var("NATIVE_DEBUG_DUMP") {
if x == "1" || x == "true" {
Expand Down
8 changes: 4 additions & 4 deletions src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,7 @@ mod tests {
fn test_invoke_dynamic_aot_native_executor(program: Program) {
let native_context = NativeContext::new();
let module = native_context
.compile(&program, false, Some(Default::default()))
.compile(&program, false, Some(Default::default()), None)
.expect("failed to compile context");
let executor = AotNativeExecutor::from_native_module(module, OptLevel::default()).unwrap();

Expand All @@ -738,7 +738,7 @@ mod tests {
fn test_invoke_dynamic_jit_native_executor(program: Program) {
let native_context = NativeContext::new();
let module = native_context
.compile(&program, false, None)
.compile(&program, false, None, None)
.expect("failed to compile context");
let executor = JitNativeExecutor::from_native_module(module, OptLevel::default()).unwrap();

Expand All @@ -756,7 +756,7 @@ mod tests {
fn test_invoke_contract_dynamic_aot(starknet_program: Program) {
let native_context = NativeContext::new();
let module = native_context
.compile(&starknet_program, false, Some(Default::default()))
.compile(&starknet_program, false, Some(Default::default()), None)
.expect("failed to compile context");
let executor = AotNativeExecutor::from_native_module(module, OptLevel::default()).unwrap();

Expand Down Expand Up @@ -788,7 +788,7 @@ mod tests {
fn test_invoke_contract_dynamic_jit(starknet_program: Program) {
let native_context = NativeContext::new();
let module = native_context
.compile(&starknet_program, false, Some(Default::default()))
.compile(&starknet_program, false, Some(Default::default()), None)
.expect("failed to compile context");
let executor = JitNativeExecutor::from_native_module(module, OptLevel::default()).unwrap();

Expand Down
10 changes: 5 additions & 5 deletions src/executor/aot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ impl AotNativeExecutor {
.keep()
.map_err(io::Error::from)?;

let object_data = crate::module_to_object(&module, opt_level)?;
crate::object_to_shared_lib(&object_data, &library_path)?;
let object_data = crate::module_to_object(&module, opt_level, None)?;
crate::object_to_shared_lib(&object_data, &library_path, None)?;

Ok(Self::new(
unsafe { Library::new(&library_path)? },
Expand Down Expand Up @@ -257,7 +257,7 @@ mod tests {
fn test_invoke_dynamic(program: Program, #[case] optlevel: OptLevel) {
let native_context = NativeContext::new();
let module = native_context
.compile(&program, false, Some(Default::default()))
.compile(&program, false, Some(Default::default()), None)
.expect("failed to compile context");
let executor = AotNativeExecutor::from_native_module(module, optlevel).unwrap();

Expand All @@ -278,7 +278,7 @@ mod tests {
fn test_invoke_dynamic_with_syscall_handler(program: Program, #[case] optlevel: OptLevel) {
let native_context = NativeContext::new();
let module = native_context
.compile(&program, false, Some(Default::default()))
.compile(&program, false, Some(Default::default()), None)
.expect("failed to compile context");
let executor = AotNativeExecutor::from_native_module(module, optlevel).unwrap();

Expand Down Expand Up @@ -317,7 +317,7 @@ mod tests {
fn test_invoke_contract_dynamic(starknet_program: Program, #[case] optlevel: OptLevel) {
let native_context = NativeContext::new();
let module = native_context
.compile(&starknet_program, false, Some(Default::default()))
.compile(&starknet_program, false, Some(Default::default()), None)
.expect("failed to compile context");
let executor = AotNativeExecutor::from_native_module(module, optlevel).unwrap();

Expand Down
Loading
Loading