From 42f5aa19e2ffdb38bf6480df45e18bbdb4115634 Mon Sep 17 00:00:00 2001 From: "dennis@kobert.dev" Date: Mon, 12 Aug 2024 12:58:43 +0200 Subject: [PATCH] Add profile run to ci --- .github/workflows/profile.yaml | 85 +++++++++++++++++++ Cargo.lock | 35 ++++++++ node-graph/graph-craft/Cargo.toml | 4 + .../graph-craft/benches/compile_demo_art.rs | 56 +++++++++--- 4 files changed, 166 insertions(+), 14 deletions(-) create mode 100644 .github/workflows/profile.yaml diff --git a/.github/workflows/profile.yaml b/.github/workflows/profile.yaml new file mode 100644 index 0000000000..9596ae7814 --- /dev/null +++ b/.github/workflows/profile.yaml @@ -0,0 +1,85 @@ +name: Rust Profiling with iai-callgrind + +on: + pull_request: + branches: [ main ] + +env: + CARGO_TERM_COLOR: always + +jobs: + profile: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + + - name: Install Valgrind + run: | + sudo apt-get update + sudo apt-get install -y valgrind + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + + - name: Install iai-callgrind + run: | + cargo install iai-callgrind-runner@0.12.3 + cargo add --dev iai-callgrind@0.12.3 + + - name: Checkout main branch + run: | + git fetch origin main:main + git checkout main + + - name: Run baseline benchmarks + run: | + cargo bench --bench compile_demo_art --features=iai -- --save-baseline=main + + - name: Checkout PR branch + run: | + git checkout ${{ github.event.pull_request.head.sha }} + + - name: Run PR benchmarks + run: | + cargo bench --bench compile_demo_art --features=iai -- --baseline main > benchmark_results.txt + + - name: Parse benchmark results + id: parse_results + run: | + COMMENT="## Performance Benchmark Results\n\n" + while IFS= read -r line; do + if [[ $line == *"Comparison with"* ]]; then + COMMENT+="### $line\n" + elif [[ $line == *"Instructions:"* || $line == *"L1 Hits:"* || $line == *"Estimated Cycles:"* ]]; then + COMMENT+="$line\n" + fi + done < benchmark_results.txt + echo "comment<> $GITHUB_OUTPUT + echo -e "$COMMENT" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Comment PR + uses: actions/github-script@v6 + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.name, + body: ${{steps.parse_results.outputs.comment}} + }) diff --git a/Cargo.lock b/Cargo.lock index b00932b821..dd731ccd3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2417,6 +2417,7 @@ dependencies = [ "glob", "graph-craft", "graphene-core", + "iai-callgrind", "js-sys", "log", "num-traits", @@ -3042,6 +3043,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "iai-callgrind" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "283f598a969822c70af13aae1272ba09c97014c7344d3b24652e5b1d7b771c36" +dependencies = [ + "bincode", + "iai-callgrind-macros", + "iai-callgrind-runner", +] + +[[package]] +name = "iai-callgrind-macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e2ff2e86bdba764b66d94b65f2caa03da60d971a6930fdc2e67f12582c5bb8" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.72", +] + +[[package]] +name = "iai-callgrind-runner" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb92a65def0d3a0ef41029c411dc2ecdd3518708c062f8bd576fd4143be1c56b" +dependencies = [ + "serde", +] + [[package]] name = "iana-time-zone" version = "0.1.60" diff --git a/node-graph/graph-craft/Cargo.toml b/node-graph/graph-craft/Cargo.toml index a03d85364d..798790a846 100644 --- a/node-graph/graph-craft/Cargo.toml +++ b/node-graph/graph-craft/Cargo.toml @@ -11,6 +11,8 @@ dealloc_nodes = [] wgpu = [] tokio = ["dep:tokio"] wayland = [] +criterion = [] +iai = [] [dependencies] # Local dependencies @@ -57,12 +59,14 @@ glob = "0.3" pprof = { version = "0.13", features = ["flamegraph"] } serde_json = { workspace = true } graph-craft = { workspace = true, features = ["serde"] } +iai-callgrind = "0.12.3" [[bench]] name = "compile_demo_art" harness = false + # [[bench]] # name = "exec_demo_art" # harness = false diff --git a/node-graph/graph-craft/benches/compile_demo_art.rs b/node-graph/graph-craft/benches/compile_demo_art.rs index 173fef26a0..b5ee3b8850 100644 --- a/node-graph/graph-craft/benches/compile_demo_art.rs +++ b/node-graph/graph-craft/benches/compile_demo_art.rs @@ -1,27 +1,55 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; use graph_craft::{document::NodeNetwork, graphene_compiler::Compiler, proto::ProtoNetwork}; +use std::path::PathBuf; -pub fn compile_to_proto(c: &mut Criterion) { - let artworks = glob::glob("../../demo-artwork/*.graphite").expect("failed to read glob pattern"); - for path in artworks { - let Ok(path) = path else { continue }; - let content = std::fs::read(&path).expect("failed to read file"); - let network = load_network(std::str::from_utf8(&content).unwrap()); - let name = path.file_stem().unwrap().to_str().unwrap(); +#[cfg(feature = "criterion")] +use criterion::{black_box, criterion_group, criterion_main, Criterion}; - c.bench_function(name, |b| b.iter_batched(|| network.clone(), |network| compile(black_box(network)), criterion::BatchSize::SmallInput)); - } -} +#[cfg(feature = "iai")] +use iai_callgrind::{black_box, library_benchmark, library_benchmark_group, main}; fn load_network(document_string: &str) -> NodeNetwork { - let document: serde_json::Value = serde_json::from_str(&document_string).expect("Failed to parse document"); - let network = serde_json::from_value::(document["network_interface"]["network"].clone()).expect("Failed to parse document"); - network + let document: serde_json::Value = serde_json::from_str(document_string).expect("Failed to parse document"); + serde_json::from_value::(document["network_interface"]["network"].clone()).expect("Failed to parse document") } + fn compile(network: NodeNetwork) -> ProtoNetwork { let compiler = Compiler {}; compiler.compile_single(network).unwrap() } +fn bench_compile(path: PathBuf) { + let content = std::fs::read(&path).expect("failed to read file"); + let network = load_network(std::str::from_utf8(&content).unwrap()); + black_box(compile(black_box(network))); +} + +#[cfg(feature = "criterion")] +fn compile_to_proto(c: &mut Criterion) { + let artworks = glob::glob("../../demo-artwork/*.graphite").expect("failed to read glob pattern"); + for path in artworks { + let Ok(path) = path else { continue }; + let name = path.file_stem().unwrap().to_str().unwrap(); + c.bench_function(name, |b| b.iter(|| bench_compile(path.clone()))); + } +} + +#[cfg_attr(feature = "iai", library_benchmark)] +fn iai_compile_to_proto() { + let artworks = glob::glob("../../demo-artwork/*.graphite").expect("failed to read glob pattern"); + for path in artworks { + let Ok(path) = path else { continue }; + bench_compile(path); + } +} + +#[cfg(feature = "criterion")] criterion_group!(benches, compile_to_proto); + +#[cfg(feature = "criterion")] criterion_main!(benches); + +#[cfg(feature = "iai")] +library_benchmark_group!(name = compile_group; benchmarks = iai_compile_to_proto); + +#[cfg(feature = "iai")] +main!(library_benchmark_groups = compile_group);