Skip to content

Commit e6566fe

Browse files
added basic benchmarker for the CairoProgramRunner running simple bootloader on cairo pies + script for deploying it
on many pies. also for run_and_prove
1 parent 5641002 commit e6566fe

File tree

11 files changed

+473
-0
lines changed

11 files changed

+473
-0
lines changed

crates/cairo-program-runner-lib/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ name = "cairo-program-runner-lib"
33
version = "0.1.0"
44
edition = "2021"
55

6+
[[bin]]
7+
name = "benchmark_runner"
8+
path = "scripts/benchmark_runner.rs"
9+
10+
[[bin]]
11+
name = "benchmark_run_and_prove"
12+
path = "scripts/benchmark_run_and_prove.rs"
613

714
[dependencies]
815
bincode.workspace = true
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// benchmark_run_and_prove.rs
2+
// Usage: cargo run --bin benchmark_run_and_prove <path_to_pie_file> [--show-output]
3+
//
4+
// This program benchmarks proving a Cairo PIE file using the simple bootloader as the program
5+
// and the PIE as the program input. It prints load/prove times and proof status.
6+
7+
use std::error::Error;
8+
use std::fs::File;
9+
use std::io::Write;
10+
use std::path::PathBuf;
11+
use std::process::Command;
12+
use std::time::Instant;
13+
14+
fn main() -> Result<(), Box<dyn Error>> {
15+
let project_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
16+
let simple_bootloader_compiled_path =
17+
project_dir.join("resources/compiled_programs/bootloaders/simple_bootloader_compiled.json");
18+
19+
// Print and check for simple_bootloader_compiled.json
20+
println!(
21+
"Looking for simple_bootloader_compiled.json at: {:?}",
22+
simple_bootloader_compiled_path
23+
);
24+
if !simple_bootloader_compiled_path.exists() {
25+
eprintln!(
26+
"ERROR: File does not exist: {:?}",
27+
simple_bootloader_compiled_path
28+
);
29+
}
30+
31+
// Parse command line args
32+
let args: Vec<String> = std::env::args().collect();
33+
let mut pie_path = None;
34+
let mut show_output = false;
35+
for (i, arg) in args.iter().enumerate() {
36+
if arg == "--show-output" {
37+
show_output = true;
38+
} else if i > 0 && !arg.starts_with("--") {
39+
pie_path = Some(arg.clone());
40+
}
41+
}
42+
let pie_path = match pie_path {
43+
Some(p) => p,
44+
None => {
45+
eprintln!("Usage: {} <path_to_pie_file> [--show-output]", args[0]);
46+
std::process::exit(1);
47+
}
48+
};
49+
50+
println!("Proving PIE: {}", pie_path);
51+
let start = Instant::now();
52+
53+
// Build the correct program_input JSON for a PIE file
54+
let program_input_json = format!(
55+
r#"{{
56+
"tasks": [
57+
{{
58+
"type": "CairoPiePath",
59+
"path": "{}",
60+
"program_hash_function": "blake"
61+
}}
62+
],
63+
"single_page": true
64+
}}"#,
65+
pie_path
66+
);
67+
// Write the JSON to a temporary file
68+
let tmp_input_path = "temp_pie_input.json";
69+
let mut tmp_file = File::create(tmp_input_path)?;
70+
tmp_file.write_all(program_input_json.as_bytes())?;
71+
72+
// Get absolute path to prover_params.json
73+
let prover_params_path = project_dir
74+
.parent()
75+
.unwrap()
76+
.join("stwo_run_and_prove/resources/prover_params.json");
77+
println!(
78+
"Looking for prover_params.json at: {:?}",
79+
prover_params_path
80+
);
81+
if !prover_params_path.exists() {
82+
eprintln!("ERROR: File does not exist: {:?}", prover_params_path);
83+
}
84+
85+
// Use the full path to the stwo_run_and_prove binary in target/release (fix path: remove
86+
// 'crates/')
87+
let stwo_bin_path = project_dir
88+
.parent()
89+
.unwrap()
90+
.parent()
91+
.unwrap()
92+
.join("target/release/stwo_run_and_prove");
93+
println!(
94+
"Using stwo_run_and_prove binary at: {}",
95+
stwo_bin_path.display()
96+
);
97+
if !stwo_bin_path.exists() {
98+
eprintln!(
99+
"ERROR: stwo_run_and_prove binary not found at {}",
100+
stwo_bin_path.display()
101+
);
102+
std::process::exit(1);
103+
}
104+
105+
// Print the command being run for debugging (for display only)
106+
println!(
107+
"Running command: {} --program=\"{}\" --program_input=\"{}\" --proofs_dir=temp_proofs --proof-format=cairo-serde --verify --prover_params_json=\"{}\" --program_output=temp_outputs",
108+
stwo_bin_path.display(),
109+
simple_bootloader_compiled_path.display(),
110+
tmp_input_path,
111+
prover_params_path.display()
112+
);
113+
114+
// Call stwo_run_and_prove binary with the bootloader as program and the PIE as program_input
115+
let output = Command::new(&stwo_bin_path)
116+
.arg(format!(
117+
"--program={}",
118+
simple_bootloader_compiled_path.display()
119+
))
120+
.arg(format!("--program_input={}", tmp_input_path))
121+
.arg("--proofs_dir=temp_proofs")
122+
.arg("--proof-format=cairo-serde")
123+
.arg("--verify")
124+
.arg(format!(
125+
"--prover_params_json={}",
126+
prover_params_path.display()
127+
))
128+
.arg("--program_output=temp_outputs")
129+
.output()?;
130+
let elapsed = start.elapsed();
131+
if output.status.success() {
132+
println!(
133+
"SUCCESS: Proved {} in {:.3} seconds",
134+
pie_path,
135+
elapsed.as_secs_f64()
136+
);
137+
if show_output {
138+
println!("stdout:\n{}", String::from_utf8_lossy(&output.stdout));
139+
}
140+
} else {
141+
println!(
142+
"ERROR: Failed to prove {} (exit code {:?})",
143+
pie_path,
144+
output.status.code()
145+
);
146+
println!("stderr:\n{}", String::from_utf8_lossy(&output.stderr));
147+
}
148+
Ok(())
149+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//! Benchmark Runner for Cairo PIE Files
2+
//!
3+
//! This program measures the performance of loading and executing Cairo PIEs
4+
//! wrapped in simple bootloader and ran by the Cairo Program Runner.
5+
//!
6+
//! Usage:
7+
//! cargo run --bin benchmark_runner <path_to_pie_file> [--show-output]
8+
//!
9+
//! Arguments:
10+
//! <path_to_pie_file> Path to the Cairo PIE file (.zip) to benchmark
11+
//! --show-output Optional flag to display program output
12+
//!
13+
//! Output:
14+
//! - Program load time
15+
//! - Program execution time
16+
//! - Total runtime
17+
//! - Execution resources (steps, memory holes, builtin counters)
18+
//!
19+
//! This binary is typically run through the run_all_pies.sh script, which provides
20+
//! additional features:
21+
//! - Running multiple PIE files
22+
//! - Testing different Rust optimization levels
23+
//! - Aggregating results into a single file
24+
//!
25+
//! Example:
26+
//! Direct usage:
27+
//! cargo run --bin benchmark_runner path/to/program.zip
28+
//! Via script:
29+
//! ./scripts/run_all_pies.sh --opt-levels=3 --pie-dir=path/to/pies
30+
31+
use std::error::Error;
32+
use std::path::PathBuf;
33+
use std::time::Instant;
34+
35+
use cairo_program_runner_lib::cairo_run_program;
36+
use cairo_program_runner_lib::types::RunMode;
37+
use cairo_vm::types::layout_name::LayoutName;
38+
use cairo_vm::types::program::Program;
39+
use cairo_vm::vm::runners::cairo_pie::CairoPie;
40+
41+
fn main() -> Result<(), Box<dyn Error>> {
42+
let project_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
43+
let simple_bootloader_compiled_path =
44+
project_dir.join("resources/compiled_programs/bootloaders/simple_bootloader_compiled.json");
45+
46+
// Parse command line args
47+
let args: Vec<String> = std::env::args().collect();
48+
let mut program_path = String::new();
49+
let mut show_output = false;
50+
51+
// Simple arg parsing
52+
for (i, arg) in args.iter().enumerate() {
53+
if arg == "--show-output" {
54+
show_output = true;
55+
} else if i > 0 && !arg.starts_with("--") {
56+
program_path = arg.clone();
57+
}
58+
}
59+
60+
if program_path.is_empty() {
61+
program_path = project_dir
62+
.join("examples/fibonacci.json")
63+
.display()
64+
.to_string();
65+
}
66+
67+
println!("Loading program from: {}", program_path);
68+
69+
// Try to load as a PIE first, if that fails, try as a regular program
70+
let start_load = Instant::now();
71+
let program_input_contents = if program_path.ends_with(".zip") {
72+
// Load as PIE - we just need to verify it loads
73+
// TODO(idanh): Add an option to configure the hash function
74+
let _cairo_pie = CairoPie::read_zip_file(&PathBuf::from(&program_path))?;
75+
format!(
76+
r#"{{
77+
"tasks": [
78+
{{
79+
"type": "CairoPiePath",
80+
"path": "{}",
81+
"program_hash_function": "pedersen"
82+
}}
83+
],
84+
"single_page": true
85+
}}"#,
86+
program_path
87+
)
88+
} else {
89+
// Load as regular program - we just need to verify it loads
90+
let _program = Program::from_file(PathBuf::from(&program_path).as_path(), Some("main"))?;
91+
format!(
92+
r#"{{
93+
"tasks": [
94+
{{
95+
"path": "{}",
96+
"program_hash_function": "pedersen",
97+
"type": "RunProgramTask"
98+
}}
99+
],
100+
"single_page": true
101+
}}"#,
102+
program_path
103+
)
104+
};
105+
let load_duration = start_load.elapsed();
106+
println!("Program loaded in: {:?}", load_duration);
107+
108+
let simple_bootloader_program =
109+
Program::from_file(simple_bootloader_compiled_path.as_path(), Some("main"))?;
110+
111+
let cairo_run_config = RunMode::Proof {
112+
layout: LayoutName::starknet_with_keccak,
113+
dynamic_layout_params: None,
114+
disable_trace_padding: false,
115+
}
116+
.create_config();
117+
118+
println!("\nExecuting program...");
119+
let start_exec = Instant::now();
120+
let mut runner = cairo_run_program(
121+
&simple_bootloader_program,
122+
Some(program_input_contents),
123+
cairo_run_config,
124+
)?;
125+
let exec_duration = start_exec.elapsed();
126+
println!("Program executed in: {:?}", exec_duration);
127+
128+
// Verify execution by checking outputs
129+
let mut output_buffer = String::new();
130+
runner.vm.write_output(&mut output_buffer)?;
131+
132+
// Print execution verification info
133+
println!("\nProgram Execution Verification:");
134+
println!("Output size: {} bytes", output_buffer.len());
135+
136+
if show_output {
137+
println!("Program output:\n{}", output_buffer);
138+
}
139+
140+
// Print execution resources
141+
println!("\nExecution Resources:");
142+
let resources = runner.get_execution_resources()?;
143+
println!("n_steps: {}", resources.n_steps);
144+
println!("n_memory_holes: {}", resources.n_memory_holes);
145+
println!(
146+
"builtin_instance_counter: {:#?}",
147+
resources.builtin_instance_counter
148+
);
149+
150+
// Check if output is empty - might indicate optimization skip
151+
if output_buffer.trim().is_empty() {
152+
println!("WARNING: Empty output might indicate optimization issues!");
153+
}
154+
155+
println!("\nBenchmark Summary:");
156+
println!("Program load time: {:?}", load_duration);
157+
println!("Program execution time: {:?}", exec_duration);
158+
println!("Total time: {:?}", load_duration + exec_duration);
159+
160+
Ok(())
161+
}

0 commit comments

Comments
 (0)