-
Notifications
You must be signed in to change notification settings - Fork 352
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: Vm Profiling #1789
base: dev
Are you sure you want to change the base?
Feat: Vm Profiling #1789
Changes from all commits
bb681e1
94d4460
465d127
3dbe1cb
24b650d
8991870
bb14e17
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,69 +56,48 @@ fn main() { | |
|
||
This will log the cycle count for `block name` and include it in the `ExecutionReport` in the `cycle_tracker` map. | ||
|
||
## Tracking Cycles with Tracing | ||
### Profiling a ZKVM program | ||
|
||
The `cycle-tracker` annotation is a convenient way to track cycles for specific sections of code. However, sometimes it can also be useful to track what functions are taking the most cycles across the entire program, without having to annotate every function individually. | ||
Profiling a zkVM program produces a profile ([example link](https://share.firefox.dev/3Om1pzz)) which makes it easy to examine program performance and see exactly where exactly VM cycles are being spent without needing to modify the program at all. | ||
|
||
First, we need to generate a trace file of the program counter at each cycle while the program is executing. This can be done by simply setting the `TRACE_FILE` environment variable with the path of the file you want to write the trace to. For example, you can run the following command in the `script` directory for any example program: | ||
To profile a program, you simply need to: | ||
1. Enable the profiling feature for `sp1-sdk` in `script/Cargo.toml` | ||
2. Set the env variable `TRACE_FILE=trace.json` and then call `ProverClient::execute()` in your script. | ||
|
||
```bash | ||
TRACE_FILE=trace.log RUST_LOG=info cargo run --release | ||
``` | ||
If you're executing a larger program (>100M cycles), you should set `TRACE_SAMPLE_RATE` to reduce the size of the trace file. A sample rate of `1000` means that 1 in every 1000 VM cycles is sampled. By default, every cycle is sampled. | ||
|
||
When the `TRACE_FILE` environment variable is set, as SP1's RISC-V runtime is executing, it will write a log of the program counter to the file specified by `TRACE_FILE`. | ||
Many examples can be found in the repo, such as this ['fibonacci'](https://github.com/succinctlabs/sp1/blob/12f212e386ae4c2da30cf6a61a7d87615d56bdac/examples/fibonacci/script/src/main.rs#L22) script. | ||
|
||
Next, we can use the `cargo prove` CLI with the `trace` command to analyze the trace file and generate a table of instruction counts. This can be done with the following command: | ||
Once you have your script it should look like the following: | ||
```rs | ||
// Execute the program using the `ProverClient.execute` method, without generating a proof. | ||
let (_, report) = client.execute(ELF, stdin.clone()).run().unwrap(); | ||
``` | ||
|
||
```bash | ||
cargo prove trace --elf <path_to_program_elf> --trace <path_to_trace_file> | ||
As well you must enable the profiling feature on the SDK: | ||
```toml | ||
sp1-sdk = { version = "3.0.0", features = ["profiling"] } | ||
``` | ||
|
||
The `trace` command will generate a table of instruction counts, sorted by the number of cycles spent in each function. The output will look something like this: | ||
The `TRACE_FILE` env var tells the executor where to save the profile, and the `TRACE_SAMPLE_RATE` env var tells the executor how often to sample the program. | ||
A larger sample rate will give you a smaller profile, it is the number of instructions in between each sample. | ||
|
||
The full command to profile should look something like this | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should just provide an end-to-end example of how to profile the fibonacci example or something. Right now these docs are a bit redundant. For example, I don't really like the overview section at the top. If you explain stuff step by step in your main explanation, I think that will probably suffice |
||
```sh | ||
TRACE_FILE=output.json TRACE_SAMPLE_RATE=100 cargo run ... | ||
``` | ||
[00:00:00] [########################################] 17053/17053 (0s) | ||
|
||
Total instructions in trace: 17053 | ||
|
||
|
||
Instruction counts considering call graph | ||
+----------------------------------------+-------------------+ | ||
| Function Name | Instruction Count | | ||
| __start | 17045 | | ||
| main | 12492 | | ||
| sp1_zkvm::syscalls::halt::syscall_halt | 4445 | | ||
| sha2::sha256::compress256 | 4072 | | ||
| sp1_lib::io::commit | 258 | | ||
| sp1_lib::io::SyscallWriter::write | 255 | | ||
| syscall_write | 195 | | ||
| memcpy | 176 | | ||
| memset | 109 | | ||
| sp1_lib::io::read_vec | 71 | | ||
| __rust_alloc | 29 | | ||
| sp1_zkvm::heap::SimpleAlloc::alloc | 22 | | ||
| syscall_hint_len | 3 | | ||
| syscall_hint_read | 2 | | ||
+----------------------------------------+-------------------+ | ||
|
||
|
||
Instruction counts ignoring call graph | ||
+----------------------------------------+-------------------+ | ||
| Function Name | Instruction Count | | ||
| main | 12075 | | ||
| sha2::sha256::compress256 | 4073 | | ||
| sp1_zkvm::syscalls::halt::syscall_halt | 219 | | ||
| memcpy | 180 | | ||
| syscall_write | 123 | | ||
| memset | 111 | | ||
| sp1_lib::io::commit | 88 | | ||
| sp1_lib::io::SyscallWriter::write | 60 | | ||
| __start | 45 | | ||
| sp1_lib::io::read_vec | 35 | | ||
| sp1_zkvm::heap::SimpleAlloc::alloc | 23 | | ||
| anonymous | 7 | | ||
| __rust_alloc | 7 | | ||
| syscall_hint_len | 4 | | ||
| syscall_hint_read | 3 | | ||
+----------------------------------------+-------------------+ | ||
|
||
To view these profiles, we recommend [Samply](https://github.com/mstange/samply). | ||
```sh | ||
cargo install --locked samply | ||
samply load output.json | ||
``` | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: capitalize Firefox, program's execution. @ctian1 thoughts on a screenshot? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. grammar mistake unsolved |
||
Samply uses the Firefox profiler to create a nice visualization of your programs execution. | ||
![An example screenshot of the Firefox Profiler](../profiling.png) | ||
|
||
#### Interpreting the Profile | ||
- The "time" measurement in the profiler is actually the number of cycles spent, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is good, it would be nice to explain what each view in the profiler is and how to interpret it, like call tree (invert call stack), flame graph, the timeline at the top There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. like some people might not know how to read the flame graph, for example There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or they might not know to invert call stack in order to find the most frequent functions |
||
in general the less cycles for a given callframe the better. | ||
|
||
- The CPU usage of the program will always be constant, as its running in the VM which is single threaded. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,5 +2,4 @@ pub mod build; | |
pub mod build_toolchain; | ||
pub mod install_toolchain; | ||
pub mod new; | ||
pub mod trace; | ||
pub mod vkey; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: ... profile. It is the ...