| name | rust-performance |
| description | Optimize Rust code for performance, understand profiling, memory layout, and zero-cost abstractions. Use when optimizing code, profiling performance, understanding memory usage, or implementing performance-critical code. Handles profiling techniques, memory optimization, SIMD usage, and performance best practices. |
Rust Performance Optimization
Guidelines for optimizing Rust code for performance.
When to Use This Skill
- Optimizing performance-critical code
- Profiling applications
- Understanding memory usage
- Using SIMD for acceleration
- Implementing zero-cost abstractions
- Benchmarking improvements
Profiling
Using perf (Linux)
perf record --call-graph=dwarf ./target/release/my_program
perf report
Using cargo-flamegraph
cargo install flamegraph
cargo flamegraph --bin my_program
Using criterion
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn bench_function(c: &mut Criterion) {
c.bench_function("my_function", |b| {
b.iter(|| black_box(my_function()))
});
}
criterion_group!(benches, bench_function);
criterion_main!(benches);
Memory Optimization
Avoid Unnecessary Allocations
fn bad_example() -> String {
let mut result = String::new();
result.push_str("prefix");
result.push_str("suffix");
result
}
fn good_example() -> String {
let mut result = String::with_capacity(13);
result.push_str("prefix");
result.push_str("suffix");
result
}
Use References Instead of Clones
fn process(data: Vec<i32>) {
let cloned = data.clone();
use_data(cloned);
}
fn process(data: &Vec<i32>) {
use_data(data);
}
Use SmallVec for Small Collections
use smallvec::SmallVec;
fn example() {
let mut vec: SmallVec<[i32; 8]> = SmallVec::new();
}
Zero-Cost Abstractions
Iterators Are Zero-Cost
let sum: i32 = vec.iter().sum();
Option/Result Are Zero-Cost
let opt: Option<i32> = Some(42);
SIMD
Using SIMD
use std::arch::x86_64::*;
#[target_feature(enable = "avx2")]
unsafe fn simd_add(a: &[f32], b: &[f32], result: &mut [f32]) {
}
Using crates like packed_simd
use packed_simd::f32x4;
fn simd_example() {
let a = f32x4::new(1.0, 2.0, 3.0, 4.0);
let b = f32x4::new(5.0, 6.0, 7.0, 8.0);
let sum = a + b;
}
Inlining
When to Inline
#[inline]
fn small_function(x: i32) -> i32 {
x * 2
}
#[inline(always)]
fn tiny_function(x: i32) -> i32 {
x + 1
}
#[inline(never)]
fn large_function() {
}
Cache-Friendly Code
Sequential Access
fn good_example(data: &[i32]) -> i32 {
let mut sum = 0;
for &value in data {
sum += value;
}
sum
}
fn bad_example(data: &[i32], indices: &[usize]) -> i32 {
let mut sum = 0;
for &index in indices {
sum += data[index];
}
sum
}
Structure of Arrays vs Array of Structures
struct Point {
x: f32,
y: f32,
z: f32,
}
let points: Vec<Point> = vec![];
struct Points {
x: Vec<f32>,
y: Vec<f32>,
z: Vec<f32>,
}
Parallelization
Using Rayon
use rayon::prelude::*;
let sum: i32 = data.par_iter().sum();
data.par_iter_mut().for_each(|x| {
*x = process(*x);
});
When to Parallelize
- Large datasets (>1000 elements)
- CPU-bound operations
- Independent operations
- Overhead is worth it
Important Rules
- Measure first: Profile before optimizing
- Optimize hot paths: Focus on code that runs frequently
- Use zero-cost abstractions: Iterators, Option, Result
- Avoid premature optimization: Write clear code first
- Use appropriate data structures: Choose based on access patterns
- Consider cache locality: Sequential access is faster
Performance Checklist
Common Patterns
✅ Good
fn process(data: &[i32]) -> Vec<i32> {
let mut result = Vec::with_capacity(data.len());
result.extend(data.iter().map(|x| x * 2));
result
}
let sum: i32 = data.iter()
.filter(|x| *x > 0)
.map(|x| x * 2)
.sum();
❌ Avoid
fn bad(data: Vec<i32>) -> Vec<i32> {
let step1: Vec<i32> = data.iter().map(|x| x * 2).collect();
let step2: Vec<i32> = step1.iter().filter(|x| *x > 10).collect();
step2
}
fn bad(data: Vec<i32>) {
let cloned = data.clone();
process(cloned);
}
Examples from Project
Look for performance optimizations in:
- Matrix operations
- Data processing pipelines
- GPU operations
- Benchmark results