mcpField
R
rust-performance

rust-performance

github.com/huiali开发4739 次安装37 周前更新
代码仓库

关于 rust-performance

rust-performance 面向优化 Rust 程序的开发者,介绍以性能分析为先的工作流,以及算法、数据结构、内存分配、缓存、并发和 NUMA 调优方法。

主要功能

  1. 01性能分析与基准测试流程
  2. 02算法和数据结构选择
  3. 03减少内存分配与克隆
  4. 04并行处理和缓存调优
  5. 05诊断锁竞争与 NUMA 影响

适用场景

  1. 01定位 CPU 或内存热点
  2. 02减少 Rust 代码的分配开销
  3. 03排查并发程序中的锁竞争
  4. 04通过基准测试评估性能改动

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/huiali/rust-skills --skill rust-performance

安装后在 Claude Code 中输入/rust-performance确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

SKILL_EN.md
SKILL_ZH.md
agents/
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 benchCriterion 基准测试
perf / flamegraphCPU 火焰图
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