|
| 1 | +--- |
| 2 | +name: rust-performance-optimization |
| 3 | +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.' |
| 4 | +argument-hint: 'What function or module needs optimization? Example: jbonsai::vocoder::mlsa::fir' |
| 5 | +--- |
| 6 | + |
| 7 | +# Rust Performance Optimization Workflow |
| 8 | + |
| 9 | +A systematic approach to safe performance improvements using profiling tools, assembly inspection, and benchmarking for jbonsai. |
| 10 | + |
| 11 | +## When to Use |
| 12 | + |
| 13 | +- Analyzing and optimizing hot code paths |
| 14 | +- Comparing performance before and after refactoring |
| 15 | +- Reducing redundant bounds checks or panic branches |
| 16 | +- Applying SIMD-friendly memory layouts |
| 17 | +- Validating that optimizations don't break correctness |
| 18 | + |
| 19 | +## Key Principles |
| 20 | + |
| 21 | +1. **No unsafe code**: Optimize within safe Rust only |
| 22 | +2. **Before/after comparison**: Always measure improvements with concrete data |
| 23 | +3. **Correctness first**: Validate every refactoring with `cargo test` |
| 24 | +4. **Data-driven decisions**: Use profiling to identify actual bottlenecks |
| 25 | + |
| 26 | +## Common Safe Optimization Patterns |
| 27 | + |
| 28 | +### 1. Move Panic Checks Outside Loops |
| 29 | + |
| 30 | +**Problem**: Repeated bounds checks in tight loops generate panic-checking code. |
| 31 | + |
| 32 | +```rust |
| 33 | +// ❌ Before: bounds check in loop |
| 34 | +for t in 0..length { |
| 35 | + for i in 1..width { |
| 36 | + result[t][i] = arr[t - i][i] * factor; // Bounds check each iteration |
| 37 | + } |
| 38 | +} |
| 39 | + |
| 40 | +// ✅ After: use .min() to prove bounds at compile time |
| 41 | +for t in 0..length { |
| 42 | + for i in 1..width.min(t + 1) { // Compiler proves: i <= t, so t - i >= 0 |
| 43 | + result[t][i] = arr[t - i][i] * factor; // No panic check needed |
| 44 | + } |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +### 2. Flatten 2D Arrays + chunks_exact |
| 49 | + |
| 50 | +**Problem**: Nested `Vec<Vec<f64>>` requires multi-level indexing and bounds checks. |
| 51 | + |
| 52 | +**Solution**: Flatten to `Vec<f64>` with `chunks_exact()` for cache efficiency and fewer panic branches. |
| 53 | + |
| 54 | +```rust |
| 55 | +// ❌ Before: nested vectors |
| 56 | +let mut wuw: Vec<Vec<f64>> = vec![vec![0.0; width]; length]; |
| 57 | +for t in 0..length { |
| 58 | + for i in 1..width { |
| 59 | + wuw[t][i] = /* ... */; |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +// ✅ After: flat with chunks_exact |
| 64 | +let mut wuw: Vec<f64> = vec![0.0; width * length]; |
| 65 | +for t in 0..length { |
| 66 | + let row = &mut wuw[t * width..(t + 1) * width]; |
| 67 | + for i in 1..width { |
| 68 | + row[i] = /* ... */; |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +// Or with split_at_mut for non-contiguous slices |
| 73 | +let (left, right) = wuw.split_at_mut(t * width); |
| 74 | +let row = &mut right[..width]; |
| 75 | +``` |
| 76 | + |
| 77 | +### 3. Pre-slice Ranges |
| 78 | + |
| 79 | +**Problem**: Computing array ranges dynamically in hot loops. |
| 80 | + |
| 81 | +```rust |
| 82 | +// ❌ Before: range computation each iteration |
| 83 | +for item in items { |
| 84 | + let slice = &data[item.start..item.end]; |
| 85 | + process(slice); |
| 86 | +} |
| 87 | + |
| 88 | +// ✅ After: pre-slice into contiguous buffer |
| 89 | +let slice = &data[start..end]; |
| 90 | +for chunk in slice.chunks_exact(chunk_size) { |
| 91 | + process(chunk); |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +## Performance Analysis Workflow |
| 96 | + |
| 97 | +### Step 1: Establish Baseline Benchmark |
| 98 | + |
| 99 | +```bash |
| 100 | +# Run the existing benchmark for your hot function |
| 101 | +cargo bench --bench bonsais |
| 102 | + |
| 103 | +# Output shows: time per iteration |
| 104 | +# Example: test bonsai ... bench: 123,456 ns/iter |
| 105 | +``` |
| 106 | + |
| 107 | +**Record the baseline number** for later comparison. |
| 108 | + |
| 109 | +### Step 2: Inspect Assembly |
| 110 | + |
| 111 | +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. |
| 112 | + |
| 113 | +**Key inspection points**: |
| 114 | +- Count panic-check calls per loop iteration |
| 115 | +- Look for redundant register computations |
| 116 | +- Identify memory access patterns |
| 117 | + |
| 118 | +### Step 3: Implement Optimization |
| 119 | + |
| 120 | +Apply one of the [Common Safe Optimization Patterns](#common-safe-optimization-patterns): |
| 121 | + |
| 122 | +- Move panic checks outside loops with `.min()` guards |
| 123 | +- Flatten 2D arrays to 1D `Vec` + `chunks_exact()` |
| 124 | +- Pre-slice ranges to avoid dynamic computation |
| 125 | +- Use `split_at_mut()` for non-overlapping mutable borrows |
| 126 | + |
| 127 | +### Step 4: Validate Correctness |
| 128 | + |
| 129 | +```bash |
| 130 | +# Run full test suite to ensure refactoring is sound |
| 131 | +cargo test --lib |
| 132 | +``` |
| 133 | + |
| 134 | +**All tests must pass** before measuring performance. |
| 135 | + |
| 136 | +### Step 5: Measure Improvement |
| 137 | + |
| 138 | +```bash |
| 139 | +# Run benchmark again with optimized code |
| 140 | +cargo bench --bench bonsais |
| 141 | + |
| 142 | +# Compare the number (typically shown as ns/iter) |
| 143 | +# Calculate: (baseline - optimized) / baseline * 100 = % improvement |
| 144 | +``` |
| 145 | + |
| 146 | +**Example**: |
| 147 | +- Before: 123,456 ns/iter |
| 148 | +- After: 98,765 ns/iter |
| 149 | +- Improvement: ~20% faster |
| 150 | + |
| 151 | +### Step 6: Advanced Profiling (Optional) |
| 152 | + |
| 153 | +For deeper analysis, install and use `cargo flamegraph`: |
| 154 | + |
| 155 | +```bash |
| 156 | +# Install flamegraph (requires perf on Linux, instruments on macOS) |
| 157 | +cargo install flamegraph |
| 158 | + |
| 159 | +# Generate flame graph (Linux: requires sudo) |
| 160 | +cargo flamegraph --bench bonsais -- --bench |
| 161 | + |
| 162 | +# Output: flamegraph.svg (open in browser) |
| 163 | +# Shows call stack frequency, identifies true bottlenecks |
| 164 | +``` |
| 165 | + |
| 166 | +**On macOS**, use Instruments.app or Swift profiler instead: |
| 167 | +```bash |
| 168 | +# Profile with macOS instruments (if available) |
| 169 | +cargo build --profile=bench |
| 170 | +xcrun xctrace record --template "System Trace" \ |
| 171 | + ./target/bench/bonsais --bench |
| 172 | +``` |
| 173 | + |
| 174 | +## Platform-Specific Considerations |
| 175 | + |
| 176 | +### x86_64 |
| 177 | + |
| 178 | +Use SIMD-friendly layouts (contiguous arrays) to benefit from: |
| 179 | +- AVX2 vectorization |
| 180 | +- Cache prefetching |
| 181 | +- Instruction-level parallelism |
| 182 | + |
| 183 | +Enable feature flags for CI: |
| 184 | +```bash |
| 185 | +RUSTFLAGS="-C target-feature=+avx2,+fma" cargo bench |
| 186 | +``` |
| 187 | + |
| 188 | +### aarch64 / ARM |
| 189 | + |
| 190 | +SIMD benefits from: |
| 191 | +- NEON vectorization |
| 192 | +- Flattened data structures |
| 193 | +- Fewer branches (predication is expensive) |
| 194 | + |
| 195 | +### macOS (Apple Silicon) |
| 196 | + |
| 197 | +- SIMD is efficient but memory layout matters more than x86_64 |
| 198 | +- Flatten arrays for better cache behavior |
| 199 | +- Test with and without `target-cpu=native` |
| 200 | + |
| 201 | +## Checklist: Safe Optimization |
| 202 | + |
| 203 | +- [ ] Baseline benchmark recorded and documented |
| 204 | +- [ ] Assembly inspected (`cargo asm`) |
| 205 | +- [ ] Optimization implemented (using safe patterns only) |
| 206 | +- [ ] `cargo test` passes 100% |
| 207 | +- [ ] Performance improvement measured and > 5% (or justified) |
| 208 | +- [ ] Code reviewed for correctness and maintainability |
| 209 | +- [ ] Commit message: note % improvement and tool used (e.g., "perf: reduce bounds checks 15%") |
| 210 | + |
| 211 | +## References |
| 212 | + |
| 213 | +- [Rust Performance Book](https://nnethercote.github.io/perf-book/) — in-depth optimization guide |
| 214 | +- [cargo-show-asm](https://github.com/pacak/cargo-show-asm) — assembly inspection |
| 215 | +- [cargo flamegraph](https://www.brendangregg.com/flamegraphs.html) — profiling methodology |
0 commit comments