diff --git a/.vscode/settings.json b/.vscode/settings.json index ad9ab59b7b..7e4fd0ca18 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,6 +24,8 @@ // // Examples. // "examples/chess/program/Cargo.toml", // "examples/chess/script/Cargo.toml", + // "examples/cycle-tracking/program/Cargo.toml", + // "examples/cycle-tracking/script/Cargo.toml", // "examples/fibonacci/program/Cargo.toml", // "examples/fibonacci/script/Cargo.toml", // "examples/io/program/Cargo.toml", diff --git a/book/writing-programs/cycle-tracking.md b/book/writing-programs/cycle-tracking.md index c641f1e57e..176249b45e 100644 --- a/book/writing-programs/cycle-tracking.md +++ b/book/writing-programs/cycle-tracking.md @@ -7,7 +7,7 @@ When writing a program, it is useful to know how many RISC-V cycles a portion of To track the number of cycles spent in a portion of the program, you can either put `println!("cycle-tracker-start: block name")` + `println!("cycle-tracker-end: block name")` statements (block name must be same between start and end) around the portion of your program you want to profile or use the `#[sp1_derive::cycle_tracker]` macro on a function. An example is shown below: ```rust,noplayground -{{#include ../../examples/cycle-tracking/program/src/main.rs}} +{{#include ../../examples/cycle-tracking/program/bin/normal.rs}} ``` Note that to use the macro, you must add the `sp1-derive` crate to your dependencies for your program. diff --git a/core/src/syscall/write.rs b/core/src/syscall/write.rs index f910e5bd2d..9c42252b93 100644 --- a/core/src/syscall/write.rs +++ b/core/src/syscall/write.rs @@ -90,17 +90,16 @@ enum CycleTrackerCommand { /// Parse a cycle tracker command from a string. If the string does not match any known command, returns None. fn parse_cycle_tracker_command(s: &str) -> Option { - if let Some((command, fn_name)) = s.split_once(':') { - let trimmed_name = fn_name.trim().to_string(); - match command { - "cycle-tracker-start" => Some(CycleTrackerCommand::Start(trimmed_name)), - "cycle-tracker-end" => Some(CycleTrackerCommand::End(trimmed_name)), - "cycle-tracker-report-start" => Some(CycleTrackerCommand::ReportStart(trimmed_name)), - "cycle-tracker-report-end" => Some(CycleTrackerCommand::ReportEnd(trimmed_name)), - _ => None, - }; + let (command, fn_name) = s.split_once(':')?; + let trimmed_name = fn_name.trim().to_string(); + + match command { + "cycle-tracker-start" => Some(CycleTrackerCommand::Start(trimmed_name)), + "cycle-tracker-end" => Some(CycleTrackerCommand::End(trimmed_name)), + "cycle-tracker-report-start" => Some(CycleTrackerCommand::ReportStart(trimmed_name)), + "cycle-tracker-report-end" => Some(CycleTrackerCommand::ReportEnd(trimmed_name)), + _ => None, } - None } /// Handle a cycle tracker command. diff --git a/examples/cycle-tracking/program/Cargo.toml b/examples/cycle-tracking/program/Cargo.toml index fbe98f6940..e0e23de020 100644 --- a/examples/cycle-tracking/program/Cargo.toml +++ b/examples/cycle-tracking/program/Cargo.toml @@ -5,6 +5,14 @@ version = "1.1.0" edition = "2021" publish = false +[[bin]] +name = "normal" +path = "bin/normal.rs" + +[[bin]] +name = "report" +path = "bin/report.rs" + [dependencies] sp1-zkvm = { path = "../../../zkvm/entrypoint" } sp1-derive = { path = "../../../derive" } diff --git a/examples/cycle-tracking/program/src/main.rs b/examples/cycle-tracking/program/bin/normal.rs similarity index 87% rename from examples/cycle-tracking/program/src/main.rs rename to examples/cycle-tracking/program/bin/normal.rs index 9dbfcbab50..cfffe0efe7 100644 --- a/examples/cycle-tracking/program/src/main.rs +++ b/examples/cycle-tracking/program/bin/normal.rs @@ -15,13 +15,13 @@ pub fn main() { let mut nums = vec![1, 1]; // Setup a large vector with Fibonacci-esque numbers. - println!("cycle-tracker-report-start: setup"); + println!("cycle-tracker-start: setup"); for _ in 0..100 { let mut c = nums[nums.len() - 1] + nums[nums.len() - 2]; c %= 7919; nums.push(c); } - println!("cycle-tracker-report-end: setup"); + println!("cycle-tracker-end: setup"); println!("cycle-tracker-start: main-body"); for i in 0..2 { diff --git a/examples/cycle-tracking/program/bin/report.rs b/examples/cycle-tracking/program/bin/report.rs new file mode 100644 index 0000000000..e60ce2e2af --- /dev/null +++ b/examples/cycle-tracking/program/bin/report.rs @@ -0,0 +1,25 @@ +#![no_main] +sp1_zkvm::entrypoint!(main); + +#[sp1_derive::cycle_tracker] +pub fn expensive_function(x: usize) -> usize { + let mut y = 1; + for _ in 0..100 { + y *= x; + y %= 7919; + } + y +} + +pub fn main() { + let mut nums = vec![1, 1]; + + // Setup a large vector with Fibonacci-esque numbers. + println!("cycle-tracker-report-start: setup"); + for _ in 0..100 { + let mut c = nums[nums.len() - 1] + nums[nums.len() - 2]; + c %= 7919; + nums.push(c); + } + println!("cycle-tracker-report-end: setup"); +} diff --git a/examples/cycle-tracking/program/elf/riscv32im-succinct-zkvm-elf b/examples/cycle-tracking/program/elf/normal similarity index 100% rename from examples/cycle-tracking/program/elf/riscv32im-succinct-zkvm-elf rename to examples/cycle-tracking/program/elf/normal diff --git a/examples/cycle-tracking/program/elf/report b/examples/cycle-tracking/program/elf/report new file mode 100755 index 0000000000..297d138f88 Binary files /dev/null and b/examples/cycle-tracking/program/elf/report differ diff --git a/examples/cycle-tracking/script/build.rs b/examples/cycle-tracking/script/build.rs index 32b99273cd..1d39b00fd7 100644 --- a/examples/cycle-tracking/script/build.rs +++ b/examples/cycle-tracking/script/build.rs @@ -1,5 +1,19 @@ -use sp1_helper::build_program; +use sp1_helper::{build_program_with_args, BuildArgs}; fn main() { - build_program("../program") + build_program_with_args( + "../program", + BuildArgs { + binary: "report".to_string(), + ..Default::default() + }, + ); + + build_program_with_args( + "../program", + BuildArgs { + binary: "normal".to_string(), + ..Default::default() + }, + ); } diff --git a/examples/cycle-tracking/script/src/main.rs b/examples/cycle-tracking/script/src/main.rs index d1da3d0c59..89722e64ac 100644 --- a/examples/cycle-tracking/script/src/main.rs +++ b/examples/cycle-tracking/script/src/main.rs @@ -1,7 +1,10 @@ use sp1_sdk::{utils, ProverClient, SP1ProofWithPublicValues, SP1Stdin}; -/// The ELF we want to execute inside the zkVM. -const ELF: &[u8] = include_bytes!("../../program/elf/riscv32im-succinct-zkvm-elf"); +/// The ELF with normal cycle tracking. +const NORMAL_ELF: &[u8] = include_bytes!("../../program/elf/normal"); + +/// The ELF with cycle tracking that gets added to the execution report. +const REPORT_ELF: &[u8] = include_bytes!("../../program/elf/report"); fn main() { // Setup a tracer for logging. @@ -10,29 +13,24 @@ fn main() { // Create an input stream. let stdin = SP1Stdin::new(); - // Generate the proof for the given program. + // Generate the proof for the cycle tracking program. let client = ProverClient::new(); - let (_, report) = client.execute(ELF, stdin.clone()).run().expect("execution failed"); - - println!("{}", report.cycle_tracker.get("setup").unwrap()); - - let (pk, vk) = client.setup(ELF); - let proof = client.prove(&pk, stdin).run().expect("proving failed"); - - // Verify proof. - client.verify(&proof, &vk).expect("verification failed"); - - // Test a round trip of proof serialization and deserialization. - proof - .save("proof-with-pis.bin") - .expect("saving proof failed"); - let deserialized_proof = - SP1ProofWithPublicValues::load("proof-with-pis.bin").expect("loading proof failed"); - - // Verify the deserialized proof. - client - .verify(&deserialized_proof, &vk) - .expect("verification failed"); - println!("successfully generated and verified proof for the program!") + // Execute the normal ELF, which shows the cycle tracking. + let (_, report) = client + .execute(NORMAL_ELF, stdin.clone()) + .run() + .expect("execution failed"); + + // Execute the report ELF, and print the tracked cycles added to the report. + let (_, report) = client + .execute(REPORT_ELF, stdin.clone()) + .run() + .expect("execution failed"); + + // Print the cycles added to the report. + // Print all the keys from report.cycle_tracker. + for (key, value) in report.cycle_tracker { + println!("{}: {}", key, value); + } }