关于 rust-performance
rust-performance 面向优化 Rust 程序的开发者,介绍以性能分析为先的工作流,以及算法、数据结构、内存分配、缓存、并发和 NUMA 调优方法。
主要功能
- 01性能分析与基准测试流程
- 02算法和数据结构选择
- 03减少内存分配与克隆
- 04并行处理和缓存调优
- 05诊断锁竞争与 NUMA 影响
适用场景
- 01定位 CPU 或内存热点
- 02减少 Rust 代码的分配开销
- 03排查并发程序中的锁竞争
- 04通过基准测试评估性能改动
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ huiali/ rust-skills --skill rust-performance安装后在 Claude Code 中输入/rust-performance确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
SKILL_EN.mdSKILL_ZH.mdagents/openai.yaml详细介绍
优化优先级
1. Algorithm choice (10x - 1000x) ← Biggest impact
2. Data structure (2x - 10x)
3. Reduce allocations (2x - 5x)
4. Cache optimization (1.5x - 3x)
5. SIMD/parallelism (2x - 8x)
警告:过早优化是万恶之源。先让代码运行起来,再优化热点路径。
解决方案模式
模式 1:预分配
// ❌ Bad: grows dynamically
let mut vec = Vec::new();
for i in 0..1000 {
vec.push(i);
}
// ✅ Good: pre-allocate known size
let mut vec = Vec::with_capacity(1000);
for i in 0..1000 {
vec.push(i);
}
模式 2:避免不必要的克隆
// ❌ Bad: unnecessary clone
fn process(item: &Item) {
let data = item.data.clone();
// use data...
}
// ✅ Good: use reference
fn process(item: &Item) {
let data = &item.data;
// use data...
}
模式 3:批量操作
// ❌ Bad: multiple database calls
for user_id in user_ids {
db.update(user_id, status)?;
}
// ✅ Good: batch update
db.update_all(user_ids, status)?;
模式 4:小对象优化
use smallvec::SmallVec;
// ✅ No heap allocation for ≤16 items
let mut vec: SmallVec<[u8; 16]> = SmallVec::new();
模式 5:并行处理
use rayon::prelude::*;
let sum: i32 = data
.par_iter()
.map(|x| expensive_computation(x))
.sum();
性能分析工具
| 工具 | 用途 |
|---|---|
cargo bench | Criterion 基准测试 |
perf / flamegraph | CPU 火焰图 |
heaptrack | 分配跟踪 |
valgrind --tool=cachegrind | 缓存分析 |
dhat | 堆分配性能分析 |
常见优化
需要修复的反模式
| 反模式 | 问题 | 正确做法 |
|---|---|---|
| 为避免生命周期而克隆 | 性能开销 | 正确设计所有权 |
| 所有内容都装箱 | 间接访问开销 | 优先使用栈分配 |
| 小型数据使用 HashMap | 哈希开销过高 | Vec + 线性搜索 |
| 循环中拼接字符串 | O(n²) | 使用 with_capacity 或 format! |
| LinkedList | 不利于缓存 | 使用 Vec 或 VecDeque |
进阶:伪共享
症状
// ❌ Problem: multiple AtomicU64 in one struct
struct ShardCounters {
inflight: AtomicU64,
completed: AtomicU64,
}
- 单个 CPU 核心利用率达到 90% 以上
- perf 中 LLC 未命中率较高
- 大量原子读-修改-写入操作
- 增加线程后速度反而变慢
诊断
# Perf analysis
perf stat -d your_program
# Look for LLC-load-misses and locked-instrs
# Flamegraph
cargo flamegraph
# Find atomic fetch_add hotspots
解决方案:缓存行填充
// ✅ Each field in separate cache line
#[repr(align(64))]
struct PaddedAtomicU64(AtomicU64);
struct ShardCounters {
inflight: PaddedAtomicU64,
completed: PaddedAtomicU64,
}
锁竞争优化
症状
// ❌ All threads compete for single lock
let shared: Arc<Mutex<HashMap<String, usize>>> =
Arc::new(Mutex::new(HashMap::new()));
- 大部分时间花在互斥锁的加锁/解锁上
- 线程越多,性能越差
- 系统时间占比较高
解决方案:线程本地分片
// ✅ Each thread has local HashMap, merge at end
pub fn parallel_count(data: &[String], num_threads: usize)
-> HashMap<String, usize>
{
let mut handles = Vec::new();
for chunk in data.chunks(data.len() / num_threads) {
handles.push(thread::spawn(move || {
let mut local = HashMap::new();
for key in chunk {
*local.entry(key.clone()).or_insert(0) += 1;
}
local // Return local counts
}));
}
// Merge all local results
let mut result = HashMap::new();
for handle in handles {
for (k, v) in handle.join().unwrap() {
*result.entry(k).or_insert(0) += v;
}
}
result
}
NUMA 感知
问题
// Multi-socket server, memory allocated on remote NUMA node
let pool = ArenaPool::new(num_threads);
// Rayon work-stealing causes tasks to run on any thread
// Cross-NUMA access causes severe memory migration latency
解决方案
// 1. NUMA node binding
let numa_node = detect_numa_node();
let pool = NumaAwarePool::new(numa_node);
// 2. Use unified allocator (jemalloc)
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
// 3. Avoid cross-NUMA object clones
// Borrow directly, don't copy data
工具
# Check NUMA topology
numactl --hardware
# Bind to NUMA node
numactl --cpunodebind=0 --membind=0 ./my_program
数据结构选择
| 场景 | 选择 | 原因 |
|---|---|---|
| 高并发写入 | DashMap 或分片 | 减少锁竞争 |
| 读多写少 | RwLock | 读锁不会阻塞 |
| 小型数据集 | Vec + 线性搜索 | HashMap 开销更高 |
| 固定键 | Enum + 数组 | 无哈希开销 |
读多写少示例
// ✅ Many reads, few updates
struct Config {
map: RwLock<HashMap<String, ConfigValue>>,
}
impl Config {
pub fn get(&self, key: &str) -> Option<ConfigValue> {
self.map.read().unwrap().get(key).cloned()
}
pub fn update(&self, key: String, value: ConfigValue) {
self.map.write().unwrap().insert(key, value);
}
}
常见性能陷阱
| 陷阱 | 症状 | 解决方案 |
|---|---|---|
| 相邻的原子变量 | 伪共享 | #[repr(align(64))] |
| 全局 Mutex | 锁竞争 | 线程本地存储 + 合并 |
| 跨 NUMA 分配 | 内存迁移 | NUMA 感知分配 |
| 频繁进行小额分配 | 分配器压力 | 对象池 |
| 动态字符串键 | 额外分配 | 使用整数 ID |
审查清单
优化性能时:
- 已进行性能分析以确定瓶颈
- 已通过测量确认瓶颈
- 算法适用于当前使用场景
- 数据结构选择恰当
- 已移除不必要的分配
- 在有益的情况下利用并行性
- 数据布局有利于缓存
- 已尽量减少锁竞争
- 基准测试显示性能有所提升
- 代码仍然易于阅读和维护
验证命令
# Benchmark
cargo bench
# Profile with perf
perf stat -d ./target/release/your_program
# Generate flamegraph
cargo flamegraph --release
# Heap profiling
valgrind --tool=dhat ./target/release/your_program
# Cache analysis
valgrind --tool=cachegrind ./target/release/your_program
# NUMA topology
numactl --hardware
常见陷阱
1. 过早优化
症状:尚未进行性能分析就开始优化
修复:先进行性能分析,只优化热点路径
2. 微优化冷路径
症状:在很少运行的代码上花费时间
修复:聚焦热点循环(10% 的代码占用 90% 的时间)
3. 为微小收益牺牲可读性
症状:为了不到 5% 的提升编写复杂代码
修复:仅在收益显著(超过 20%)时进行优化
性能诊断工作流
1. Identify symptom (slow, high CPU, high memory)
↓
2. Profile with appropriate tool
- CPU → perf/flamegraph
- Memory → heaptrack/dhat
- Cache → cachegrind
↓
3. Find hotspot (function/line)
↓
4. Understand why it's slow
- Algorithm? Data structure? Allocation?
↓
5. Apply targeted optimization
↓
6. Benchmark to confirm improvement
↓
7. Repeat if not fast enough
相关技能
- rust-concurrency - 并行处理模式
- rust-async - 异步性能优化
- rust-unsafe - 使用 unsafe 实现零成本抽象
- rust-coding - 编写高性能的惯用代码
- rust-anti-pattern - 应避免的性能反模式
本地化参考
- 中文版:SKILL_ZH.md - 完整中文版本,包含所有内容
评论
支持 Markdown