Skip to content

Commit a123e3d

Browse files
committed
docs: add rust-performance-optimization skill
1 parent 4aa02e4 commit a123e3d

3 files changed

Lines changed: 455 additions & 2 deletions

File tree

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
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

Comments
 (0)