From f13bcd19c7ce499733c32c7e9921f85ea072795e Mon Sep 17 00:00:00 2001 From: cm-ayf Date: Sat, 4 Jul 2026 22:38:53 +0900 Subject: [PATCH 1/2] docs: add rust-performance-optimization skill --- .../rust-performance-optimization/SKILL.md | 215 ++++++++++++++++ .../references/tools.md | 238 ++++++++++++++++++ Cargo.toml | 4 +- 3 files changed, 455 insertions(+), 2 deletions(-) create mode 100644 .github/skills/rust-performance-optimization/SKILL.md create mode 100644 .github/skills/rust-performance-optimization/references/tools.md diff --git a/.github/skills/rust-performance-optimization/SKILL.md b/.github/skills/rust-performance-optimization/SKILL.md new file mode 100644 index 0000000..164d9d1 --- /dev/null +++ b/.github/skills/rust-performance-optimization/SKILL.md @@ -0,0 +1,215 @@ +--- +name: rust-performance-optimization +description: 'Optimize Rust code performance using safe patterns and profiling tools. Use when: analyzing hot loops, reducing bounds-check panics, comparing before/after implementations, measuring improvements with cargo bench and cargo asm.' +argument-hint: 'What function or module needs optimization? Example: jbonsai::vocoder::mlsa::fir' +--- + +# Rust Performance Optimization Workflow + +A systematic approach to safe performance improvements using profiling tools, assembly inspection, and benchmarking for jbonsai. + +## When to Use + +- Analyzing and optimizing hot code paths +- Comparing performance before and after refactoring +- Reducing redundant bounds checks or panic branches +- Applying SIMD-friendly memory layouts +- Validating that optimizations don't break correctness + +## Key Principles + +1. **No unsafe code**: Optimize within safe Rust only +2. **Before/after comparison**: Always measure improvements with concrete data +3. **Correctness first**: Validate every refactoring with `cargo test` +4. **Data-driven decisions**: Use profiling to identify actual bottlenecks + +## Common Safe Optimization Patterns + +### 1. Move Panic Checks Outside Loops + +**Problem**: Repeated bounds checks in tight loops generate panic-checking code. + +```rust +// ❌ Before: bounds check in loop +for t in 0..length { + for i in 1..width { + result[t][i] = arr[t - i][i] * factor; // Bounds check each iteration + } +} + +// ✅ After: use .min() to prove bounds at compile time +for t in 0..length { + for i in 1..width.min(t + 1) { // Compiler proves: i <= t, so t - i >= 0 + result[t][i] = arr[t - i][i] * factor; // No panic check needed + } +} +``` + +### 2. Flatten 2D Arrays + chunks_exact + +**Problem**: Nested `Vec>` requires multi-level indexing and bounds checks. + +**Solution**: Flatten to `Vec` with `chunks_exact()` for cache efficiency and fewer panic branches. + +```rust +// ❌ Before: nested vectors +let mut wuw: Vec> = vec![vec![0.0; width]; length]; +for t in 0..length { + for i in 1..width { + wuw[t][i] = /* ... */; + } +} + +// ✅ After: flat with chunks_exact +let mut wuw: Vec = vec![0.0; width * length]; +for t in 0..length { + let row = &mut wuw[t * width..(t + 1) * width]; + for i in 1..width { + row[i] = /* ... */; + } +} + +// Or with split_at_mut for non-contiguous slices +let (left, right) = wuw.split_at_mut(t * width); +let row = &mut right[..width]; +``` + +### 3. Pre-slice Ranges + +**Problem**: Computing array ranges dynamically in hot loops. + +```rust +// ❌ Before: range computation each iteration +for item in items { + let slice = &data[item.start..item.end]; + process(slice); +} + +// ✅ After: pre-slice into contiguous buffer +let slice = &data[start..end]; +for chunk in slice.chunks_exact(chunk_size) { + process(chunk); +} +``` + +## Performance Analysis Workflow + +### Step 1: Establish Baseline Benchmark + +```bash +# Run the existing benchmark for your hot function +cargo bench --bench bonsais + +# Output shows: time per iteration +# Example: test bonsai ... bench: 123,456 ns/iter +``` + +**Record the baseline number** for later comparison. + +### Step 2: Inspect Assembly + +Use `cargo asm` (from `cargo-show-asm`) to see generated code and identify panic branches. See [cargo asm reference](./references/tools.md#cargo-asm-assembly-inspection) for detailed usage. + +**Key inspection points**: +- Count panic-check calls per loop iteration +- Look for redundant register computations +- Identify memory access patterns + +### Step 3: Implement Optimization + +Apply one of the [Common Safe Optimization Patterns](#common-safe-optimization-patterns): + +- Move panic checks outside loops with `.min()` guards +- Flatten 2D arrays to 1D `Vec` + `chunks_exact()` +- Pre-slice ranges to avoid dynamic computation +- Use `split_at_mut()` for non-overlapping mutable borrows + +### Step 4: Validate Correctness + +```bash +# Run full test suite to ensure refactoring is sound +cargo test --lib +``` + +**All tests must pass** before measuring performance. + +### Step 5: Measure Improvement + +```bash +# Run benchmark again with optimized code +cargo bench --bench bonsais + +# Compare the number (typically shown as ns/iter) +# Calculate: (baseline - optimized) / baseline * 100 = % improvement +``` + +**Example**: +- Before: 123,456 ns/iter +- After: 98,765 ns/iter +- Improvement: ~20% faster + +### Step 6: Advanced Profiling (Optional) + +For deeper analysis, install and use `cargo flamegraph`: + +```bash +# Install flamegraph (requires perf on Linux, instruments on macOS) +cargo install flamegraph + +# Generate flame graph (Linux: requires sudo) +cargo flamegraph --bench bonsais -- --bench + +# Output: flamegraph.svg (open in browser) +# Shows call stack frequency, identifies true bottlenecks +``` + +**On macOS**, use Instruments.app or Swift profiler instead: +```bash +# Profile with macOS instruments (if available) +cargo build --profile=bench +xcrun xctrace record --template "System Trace" \ + ./target/bench/bonsais --bench +``` + +## Platform-Specific Considerations + +### x86_64 + +Use SIMD-friendly layouts (contiguous arrays) to benefit from: +- AVX2 vectorization +- Cache prefetching +- Instruction-level parallelism + +Enable feature flags for CI: +```bash +RUSTFLAGS="-C target-feature=+avx2,+fma" cargo bench +``` + +### aarch64 / ARM + +SIMD benefits from: +- NEON vectorization +- Flattened data structures +- Fewer branches (predication is expensive) + +### macOS (Apple Silicon) + +- SIMD is efficient but memory layout matters more than x86_64 +- Flatten arrays for better cache behavior +- Test with and without `target-cpu=native` + +## Checklist: Safe Optimization + +- [ ] Baseline benchmark recorded and documented +- [ ] Assembly inspected (`cargo asm`) +- [ ] Optimization implemented (using safe patterns only) +- [ ] `cargo test` passes 100% +- [ ] Performance improvement measured and > 5% (or justified) +- [ ] Code reviewed for correctness and maintainability +- [ ] Commit message: note % improvement and tool used (e.g., "perf: reduce bounds checks 15%") + +## References + +- [Rust Performance Book](https://nnethercote.github.io/perf-book/) — in-depth optimization guide +- [cargo-show-asm](https://github.com/pacak/cargo-show-asm) — assembly inspection +- [cargo flamegraph](https://www.brendangregg.com/flamegraphs.html) — profiling methodology diff --git a/.github/skills/rust-performance-optimization/references/tools.md b/.github/skills/rust-performance-optimization/references/tools.md new file mode 100644 index 0000000..a5cbc91 --- /dev/null +++ b/.github/skills/rust-performance-optimization/references/tools.md @@ -0,0 +1,238 @@ +# Profiling Tools Reference + +Quick command reference for cargo bench, cargo asm, and flamegraph. + +## cargo bench (Rust Nightly Benchmarks) + +**Purpose**: Measure iteration performance using unstable bench feature. + +### Installation + +Already configured in Cargo.toml with `#![feature(test)]` in benches/. + +### Basic Usage + +```bash +# Run all benchmarks +cargo bench --bench bonsais + +# Output format +# test bonsai ... bench: 123,456 ns/iter + +# Run specific benchmark +cargo bench --bench bonsais bonsai +``` + +### With Profile Optimization + +```bash +# Bench profile (most optimized, includes debug symbols) +cargo bench --bench bonsais --profile=bench + +# Compare to dev build +cargo bench --bench bonsais --profile=dev # Much slower, for validation only +``` + +### Warmup & Iterations + +The Bencher struct auto-runs: +1. Initial iterations to warm CPU cache +2. 25 measured iterations (default, configurable) +3. Reports mean + variance + +### Output Interpretation + +``` +test bonsai ... bench: 123,456 ns/iter (+/- 2,345 ns) + ↑ Mean time ↑ Standard deviation +``` + +**Good result**: < ±5% variance (stable) +**Poor result**: > ±10% variance (CPU throttling or background processes) + +**Pro tip**: Close other apps and disable CPU frequency scaling for stable benchmarks. + +--- + +## cargo asm (Assembly Inspection) + +**Purpose**: View generated machine code to identify panic checks and optimization opportunities. + +### Installation + +```bash +cargo install cargo-show-asm +``` + +### Basic Usage + +```bash +# View assembly for a function +cargo asm --lib module::function_name + +# Example from jbonsai +cargo asm --profile=bench --lib mlpg_adjust::mlpg::ldl_factorization +``` + +### Preventing Inlining + +Use `#[inline(never)]` to ensure accurate assembly inspection: + +```rust +#[inline(never)] +fn target_function() { + // Your hot function code +} +``` + +**Why it matters**: Without `#[inline(never)]`, the compiler may inline the function, and `cargo asm` will show the caller's context instead of the actual function body. This attribute is essential for accurately comparing before/after assembly and identifying optimization opportunities. See [src/vocoder/mlsa.rs](../../../src/vocoder/mlsa.rs#L127) for an example of test-only usage. + +### Reading Output + +Output shows Intel x86_64 syntax (default). Each line: + +``` +0x0000: 48 89 d8 mov %rbx, %rax # Move register +0x0003: 83 c0 01 add $0x1, %eax # Add immediate +0x0006: 48 83 ff 00 cmp $0x0, %rdi # Compare +0x000a: 74 10 je 0x1c # Jump if equal +``` + +### Identifying Panic Checks + +Search for patterns: + +``` +panic_bounds_check # Function call to panic handler +cmp # Comparison instruction +je / jne / jl / jge # Conditional jump (branch) +``` + +**Example - bounds check**: +```asm +48 3b 47 00 cmp 0x0(%rdi), %rax +0f 87 XX XX XX XX ja panic_bounds_check +``` +(JA = "Jump if Above", triggers panic if index >= length) + +### Counting Panic Checks + +```bash +# Count panic_bounds_check calls +cargo asm --lib module::func | grep -c panic_bounds_check + +# Before optimization: 18 +# After optimization: 2 +``` + +### Platform-Specific Assembly + +```bash +# ARM64 assembly (macOS/Linux ARM) +cargo asm --target aarch64-unknown-linux-gnu --lib module::func + +# x86 without AVX2 (baseline) +RUSTFLAGS="-C target-feature=" cargo asm --lib module::func + +# x86 with AVX2+FMA +RUSTFLAGS="-C target-feature=+avx2,+fma" cargo asm --lib module::func +``` + +--- + +## cargo flamegraph (Call Stack Profiling) + +**Purpose**: Identify where the program spends time across all call stacks. + +### Installation + +```bash +cargo install flamegraph + +# Also requires perf (Linux) or Instruments (macOS) +# Linux: sudo apt install linux-tools-generic +# macOS: Install Xcode command line tools +``` + +### Basic Usage + +```bash +# Generate flame graph (Linux) +cargo flamegraph --bench bonsais -- --bench + +# Output: flamegraph.svg (open in web browser) +``` + +### Reading Flame Graphs + +- **X-axis**: Time spent (wider = more time) +- **Y-axis**: Call stack depth (bottom = main, up = deeper calls) +- **Color**: Random (helps distinguish blocks) +- **Hotter colors in some versions**: Red = hot, blue = cold + +**Typical structure**: +``` +[bonsais-bench] + ├─ [test_bonsai] + │ ├─ engine.synthesize (70%) + │ │ ├─ vocoder.mlsa_filter (30%) + │ │ └─ mlpg_adjust.mlpg (40%) + │ └─ model.interpolate (30%) + └─ [framework overhead] (30%) +``` + +### Finding Optimization Targets + +1. **Click on wide blocks** - these are hot functions +2. **Find functions with many branches** - good candidates for bounds-check reduction +3. **Look for repeated small functions** - inline opportunities + +### macOS Profiling (Alternative) + +If flamegraph doesn't work, use Instruments: + +```bash +# Build in bench profile +cargo build --profile=bench --bench bonsais + +# Profile with Instruments +xcrun xctrace record --template "System Trace" \ + --output /tmp/trace.trace \ + ./target/bench/bonsais --bench + +# Open in Instruments.app +open /tmp/trace.trace +``` + +--- + +## Pro Tips + +### 1. Disable Frequency Scaling (for stable benchmarks) + +Linux: +```bash +echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor +``` + +macOS: Use Activity Monitor or disable using Energy Saver settings. + +### 2. Isolate Benchmark Function + +```bash +# Build only the benchmark binary (faster iteration) +cargo build --bench bonsais --profile=bench + +# Run just one benchmark test multiple times +./target/bench/bonsais --bench --nightly bonsai +``` + +### 3. Compare Before/After Codegen + +```bash +# Generate LLVM IR (intermediate representation) +RUSTFLAGS="--emit llvm-ir" cargo build --profile=bench --lib + +# Find and diff LLVM files +diff target/release/deps/jbonsai-*.ll +``` diff --git a/Cargo.toml b/Cargo.toml index 7bd73f7..ed55d77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,10 +23,10 @@ rustdoc-args = ["--cfg", "docsrs"] [[example]] name = "is-bonsai" -required-features = ["binary"] +# required-features = ["binary"] [[example]] name = "genji" -required-features = ["binary"] +# required-features = ["binary"] [dependencies] byteorder = "1.5.0" From 5158bc6af1eb0143b739355fd222fb8715fea479 Mon Sep 17 00:00:00 2001 From: cm-ayf Date: Sat, 4 Jul 2026 22:44:50 +0900 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/skills/rust-performance-optimization/SKILL.md | 4 ++-- .../rust-performance-optimization/references/tools.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/skills/rust-performance-optimization/SKILL.md b/.github/skills/rust-performance-optimization/SKILL.md index 164d9d1..f30077f 100644 --- a/.github/skills/rust-performance-optimization/SKILL.md +++ b/.github/skills/rust-performance-optimization/SKILL.md @@ -166,9 +166,9 @@ cargo flamegraph --bench bonsais -- --bench **On macOS**, use Instruments.app or Swift profiler instead: ```bash # Profile with macOS instruments (if available) -cargo build --profile=bench +cargo build --profile=bench --bench bonsais xcrun xctrace record --template "System Trace" \ - ./target/bench/bonsais --bench + ./target/bench/deps/bonsais-* --bench ``` ## Platform-Specific Considerations diff --git a/.github/skills/rust-performance-optimization/references/tools.md b/.github/skills/rust-performance-optimization/references/tools.md index a5cbc91..eaeb6fb 100644 --- a/.github/skills/rust-performance-optimization/references/tools.md +++ b/.github/skills/rust-performance-optimization/references/tools.md @@ -71,7 +71,7 @@ cargo install cargo-show-asm cargo asm --lib module::function_name # Example from jbonsai -cargo asm --profile=bench --lib mlpg_adjust::mlpg::ldl_factorization +cargo asm --profile=bench --lib mlsa::fir ``` ### Preventing Inlining @@ -198,7 +198,7 @@ cargo build --profile=bench --bench bonsais # Profile with Instruments xcrun xctrace record --template "System Trace" \ --output /tmp/trace.trace \ - ./target/bench/bonsais --bench + ./target/bench/deps/bonsais-* --bench # Open in Instruments.app open /tmp/trace.trace @@ -224,7 +224,7 @@ macOS: Use Activity Monitor or disable using Energy Saver settings. cargo build --bench bonsais --profile=bench # Run just one benchmark test multiple times -./target/bench/bonsais --bench --nightly bonsai +cargo bench --bench bonsais --profile=bench bonsai ``` ### 3. Compare Before/After Codegen