rust-performance について
rust-performance は、Rust プログラムを最適化する開発者向けのスキルです。プロファイリングを起点とする手順に加え、アルゴリズム、データ構造、割り当て、キャッシュ、並行処理、NUMA の調整方法を扱います。
主な機能
- 01プロファイリングとベンチマークの手順
- 02アルゴリズムとデータ構造の選定
- 03メモリ割り当てとクローンの削減
- 04並列処理とキャッシュの調整
- 05競合と NUMA の影響の診断
活用シーン
- 01CPU やメモリのホットスポット特定
- 02Rust コードの割り当てコスト削減
- 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 | ヒープ割り当てのプロファイリング |
一般的な最適化
修正すべきアンチパターン
| アンチパターン | 問題点 | 適切な方法 |
|---|---|---|
| ライフタイムを避けるためにクローンする | パフォーマンスコスト | 適切な所有権設計 |
| すべてを Box に入れる | 間接参照のオーバーヘッド | スタック割り当てを優先 |
| 小さなデータに HashMap を使う | ハッシュのオーバーヘッドが大きすぎる | Vec + 線形検索 |
| ループ内で文字列を連結する | O(n²) | with_capacity または format! |
| LinkedList | キャッシュ効率が悪い | Vec または VecDeque |
応用:偽共有
症状
// ❌ Problem: multiple AtomicU64 in one struct
struct ShardCounters {
inflight: AtomicU64,
completed: AtomicU64,
}
- 1 つの CPU コアが 90% 以上
- perf で LLC ミス率が高い
- アトミックな RMW 操作が多い
- スレッドを追加すると遅くなる
診断
# 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()));
- 時間の大半が mutex のロック/アンロックに費やされる
- スレッド数を増やすとパフォーマンスが低下する
- システム時間の割合が高い
解決策:スレッドローカルシャーディング
// ✅ 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 + array | ハッシュのオーバーヘッドがゼロ |
読み取り中心の例
// ✅ 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 対応