forked from nexus-xyz/nexus-zkvm
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add pprof profile macro (nexus-xyz#261)
# Helper macros for Host development This crate provides a few helper macros for Host development. ## Usage Add this to your `Cargo.toml`: ```toml [dependencies] nexus-macro = { path = "../macro" } nexus-profiler = { path = "../macro/profiler" } ``` ## Profile macros ### Requirements Install `go` programming language. #### MacOS ```bash brew install go ``` #### Linux ```bash sudo apt install golang-go ``` ### How to use `#[profile]` macro At any host function, you can use `#[profile]` to profile the function. ```rust use nexus_macro::profile; #[profile] pub fn eval_inst(vm: &mut NexusVM<impl Memory>) -> Result<()> { /// ..... /// ..... } #[profile("is_satisfied.pb")] pub fn is_satisfied<C: CommitmentScheme<G>>( &self, U: &R1CSInstance<G, C>, W: &R1CSWitness<G>, pp: &C::PP, ) -> Result<(), Error> { /// ... /// .... } ``` When the profile output name is undefined, the function name is used as default with suffix `.pb`. The first macro will write the profile data to `eval_inst.pb`. The second macro will write the profile data to `is_satisfied.pb`. You can open the `.pb` file with `go tool pprof -http=127.0.0.1:8000 [function_name].pb` ### Warning *Note:* This macro is supposed to be used for one-time-call function. It won't accumulate data if you call the function multiple times. The wrong way to use this macro. ```rust #[profile] pub fn eval_inst(vm: &mut NexusVM<impl Memory>) -> Result<()> { /// ... } pub fn eval_inst_top(vm: &mut NexusVM<impl Memory>) -> Result<()> { for _ in 0..100 { eval_inst(vm)?; } } ``` The first example will only profile the last call of `eval_inst` and omit 99 calls. The right way to use this macro. ```rust pub fn eval_inst(vm: &mut NexusVM<impl Memory>) -> Result<()> { /// ... } #[profile] pub fn eval_inst_top(vm: &mut NexusVM<impl Memory>) -> Result<()> { for _ in 0..100 { eval_inst(vm)?; } } ``` The second example will profile the whole `eval_inst_top` function. Co-authored-by: duc-nx <>
- Loading branch information
Showing
12 changed files
with
265 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
[package] | ||
name = "nexus-macro" | ||
edition.workspace = true | ||
version.workspace = true | ||
authors.workspace = true | ||
homepage.workspace = true | ||
repository.workspace = true | ||
keywords.workspace = true | ||
categories.workspace = true | ||
publish.workspace = true | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[dependencies] | ||
syn = { version = "1.0", features = ["full"] } | ||
quote = "1.0" | ||
proc-macro2 = "1.0" | ||
proc-macro-crate = "3.1.0" | ||
nexus-profiler = { path = "./profiler" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
# Helper macros for Host development | ||
|
||
This crate provides a few helper macros for Host development. | ||
|
||
## Requirements | ||
|
||
Install `go` programming language. | ||
|
||
#### MacOS | ||
|
||
```bash | ||
brew install go | ||
``` | ||
|
||
#### Linux | ||
|
||
```bash | ||
sudo apt install golang-go | ||
``` | ||
|
||
|
||
## Usage | ||
|
||
### How to use `#[profile]` macro in SDK | ||
|
||
For SDK user, this macro is already provided in `Cargo.toml` in `nexus-sdk` crate. | ||
|
||
|
||
The `nexus_macro` is re-exported in `nexus-sdk`. | ||
|
||
To use `#[profile]` macro in SDK, one must import the profiler dependency: | ||
|
||
```toml | ||
[dependencies] | ||
nexus-profiler = { git = "https://github.com/nexus-xyz/nexus-zkvm/macro/profiler" } | ||
``` | ||
|
||
```rust | ||
use nexus_sdk::nexus_macro::profile; | ||
|
||
#[profile] | ||
fn compile_program(opts: CompileOpts) { | ||
Nova::compile(&opts).expect("failed to compile guest program") | ||
} | ||
``` | ||
|
||
In SDK, you must include the `use sdk::nexus_macro::profile;` to use the `#[profile]` macro. | ||
The code above will generate 1 file `compile_program.pb` in the current directory. | ||
|
||
You can open the `.pb` file with `go tool pprof -http=127.0.0.1:8000 compile_program.pb` | ||
|
||
|
||
### How to use `#[profile]` macro in development | ||
|
||
For Nexus developer, if you want to use this macro in your crate. | ||
Add this to your crate `Cargo.toml`: | ||
|
||
```toml | ||
[dependencies] | ||
nexus-macro = { path = "../macro/" } | ||
nexus-profiler = { path = "../macro/profiler" } | ||
``` | ||
|
||
At any host function, you can use `#[profile]` to profile the function. | ||
|
||
```rust | ||
use nexus_macro::profile; | ||
|
||
#[profile] | ||
pub fn eval_inst(vm: &mut NexusVM<impl Memory>) -> Result<()> { | ||
/// ..... | ||
/// ..... | ||
} | ||
|
||
#[profile("is_satisfied.pb")] | ||
pub fn is_satisfied<C: CommitmentScheme<G>>( | ||
&self, | ||
U: &R1CSInstance<G, C>, | ||
W: &R1CSWitness<G>, | ||
pp: &C::PP, | ||
) -> Result<(), Error> { | ||
/// ... | ||
/// .... | ||
} | ||
|
||
``` | ||
|
||
When the profile output name is undefined, the function name is used as default with suffix `.pb`. | ||
The first macro will write the profile data to `eval_inst.pb`. The second macro will write the profile data to `is_satisfied.pb`. | ||
|
||
Build with `cargo build --release` to build in release mode. | ||
|
||
You can open the `.pb` file with `go tool pprof -http=127.0.0.1:8000 [function_name].pb` | ||
|
||
|
||
### Warning | ||
|
||
*Note:* This macro is supposed to be used for one-time-call function. It won't accumulate data if you call the function multiple times. | ||
|
||
The wrong way to use this macro. | ||
|
||
```rust | ||
#[profile] | ||
pub fn eval_inst(vm: &mut NexusVM<impl Memory>) -> Result<()> { | ||
/// ... | ||
} | ||
|
||
pub fn eval_inst_top(vm: &mut NexusVM<impl Memory>) -> Result<()> { | ||
for _ in 0..100 { | ||
eval_inst(vm)?; | ||
} | ||
} | ||
``` | ||
The first example will only profile the last call of `eval_inst` and omit 99 calls. | ||
|
||
|
||
The right way to use this macro. | ||
|
||
```rust | ||
pub fn eval_inst(vm: &mut NexusVM<impl Memory>) -> Result<()> { | ||
/// ... | ||
} | ||
|
||
#[profile] | ||
pub fn eval_inst_top(vm: &mut NexusVM<impl Memory>) -> Result<()> { | ||
for _ in 0..100 { | ||
eval_inst(vm)?; | ||
} | ||
} | ||
``` | ||
|
||
The second example will profile the whole `eval_inst_top` function. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
target | ||
Cargo.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "nexus-profiler" | ||
edition.workspace = true | ||
version.workspace = true | ||
authors.workspace = true | ||
homepage.workspace = true | ||
repository.workspace = true | ||
keywords.workspace = true | ||
categories.workspace = true | ||
publish.workspace = true | ||
|
||
[dependencies] | ||
pprof = { version = "0.13", features = ["prost-codec", "frame-pointer"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Nexus Profiler | ||
|
||
This is a profiling module for the Nexus project. | ||
|
||
The goal is to provide a simple and easy-to-use profiling solution for Nexus, allowing developers to quickly identify performance bottlenecks and optimize their code. | ||
|
||
## Features | ||
|
||
- `pprof` profiler via `nexus_macro::profile` macro | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#[cfg(any(target_os = "macos", target_os = "linux"))] | ||
pub mod profiler; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use std::fs::File; | ||
use std::io::Write; | ||
|
||
use pprof::{protos::Message, ProfilerGuard}; | ||
|
||
pub fn pprof_start() -> ProfilerGuard<'static> { | ||
pprof::ProfilerGuardBuilder::default() | ||
.frequency(1000) | ||
.blocklist(&["libc", "libgcc", "pthread", "vdso"]) | ||
.build() | ||
.expect("Failed to start profiler") | ||
} | ||
|
||
pub fn pprof_end(guard: ProfilerGuard<'static>, filename: &str) { | ||
let report = guard | ||
.report() | ||
.build() | ||
.expect("Failed to pprof build report"); | ||
let profile = report.pprof().expect("Failed to generate pprof profile"); | ||
|
||
let mut content = Vec::new(); | ||
profile | ||
.encode(&mut content) | ||
.expect("Failed to encode pprof profile"); | ||
|
||
let mut file = File::create(filename).expect("Failed to create pprof profile file"); | ||
file.write_all(&content) | ||
.expect("Failed to write pprof profile data"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
use proc_macro::TokenStream; | ||
mod pprof; | ||
|
||
#[proc_macro_attribute] | ||
pub fn profile(attr: TokenStream, input: TokenStream) -> TokenStream { | ||
pprof::derive(attr, input) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
use proc_macro::TokenStream; | ||
use proc_macro2::Span; | ||
use proc_macro_crate::{crate_name, FoundCrate}; | ||
use quote::quote; | ||
use syn::{parse_macro_input, Ident, ItemFn, LitStr}; | ||
|
||
pub fn derive(attr: TokenStream, input: TokenStream) -> TokenStream { | ||
let input_fn = parse_macro_input!(input as ItemFn); | ||
let vis = input_fn.vis; | ||
let sig = input_fn.sig; | ||
let block = input_fn.block; | ||
|
||
let file_name = if attr.is_empty() { | ||
let function_name = format!("{}.pb", sig.ident); | ||
LitStr::new(&function_name, Span::call_site()) | ||
} else { | ||
parse_macro_input!(attr as LitStr) | ||
}; | ||
|
||
let found_crate = crate_name("nexus-profiler").expect("profiler is not in `Cargo.toml`"); | ||
|
||
let profiler = match found_crate { | ||
FoundCrate::Itself => quote!(crate::profiler), | ||
FoundCrate::Name(name) => { | ||
let ident = Ident::new(&name, Span::call_site()); | ||
quote!( #ident::profiler ) | ||
} | ||
}; | ||
|
||
let output: proc_macro2::TokenStream = quote! { | ||
#vis #sig { | ||
let guard = #profiler::pprof_start(); | ||
let result = (|| #block)(); | ||
#profiler::pprof_end(guard, #file_name); | ||
result | ||
} | ||
}; | ||
|
||
output.into() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters