// article
Rust パフォーマンスチューニングの基本.
Rust パフォーマンスチューニングの基本
Rust は高速ですが、不適切な実装ではパフォーマンスが低下します。効果的なチューニング方法を学びましょう。
1. リリースビルド使用時のパフォーマンス差
# デバッグビルド(遅い)
cargo build
# リリースビルド(最適化済み)
cargo build --release
| ビルドモード | 最適化 | 実行速度 | コンパイル時間 |
|---|---|---|---|
| Debug | なし | 遅い | 短い |
| Release | あり | 速い | 長い |
2. ベンチマーク測定
Cargo.toml:
[[bench]]
name = "benchmarks"
harness = false
benches/benchmarks.rs:
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn fibonacci(n: u32) -> u32 {
match n {
0 | 1 => n,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
fn fibonacci_benchmark(c: &mut Criterion) {
c.bench_function("fib 20", |b| b.iter(|| fibonacci(black_box(20))));
}
criterion_group!(benches, fibonacci_benchmark);
criterion_main!(benches);
実行:
cargo bench
3. メモリ効率
メモリ確保(allocation)を減らす
// 遅い:毎回新しいVecを作成
fn slow_filter(items: &[i32]) -> Vec<i32> {
items.iter().filter(|x| x % 2 == 0).collect()
}
// 速い:既存のVecを再利用
fn fast_filter(items: &[i32], result: &mut Vec<i32>) {
result.clear();
for item in items {
if item % 2 == 0 {
result.push(*item);
}
}
}
イテレータの遅延評価
イテレータは遅延評価のため、中間結果が蓄積されません。本当に必要な時だけ計算されます。
4. アルゴリズム選択
大切な法則1:適切なアルゴリズムを選ぶことが、言語やコンパイラの最適化より重要です。
| アルゴリズム | 時間計算量 | 用途 |
|---|---|---|
| Linear | O(n) | 単純な探索 |
| Binary | O(log n) | ソート済みリスト |
| Hash | O(1) | ルックアップテーブル |
5. SIMD 最適化
Rust はコンパイラが自動的に SIMD を活用します:
fn vector_multiply(a: &[f32], b: &[f32]) -> Vec<f32> {
a.iter().zip(b.iter()).map(|(x, y)| x * y).collect()
}
このような単純な操作は、コンパイラが自動的に SIMD 命令に変換することがあります。
6. プロファイリングツール
# Flamegraph による可視化(macOS/Linux)
cargo install flamegraph
cargo flamegraph --release
まとめ
効果的なチューニング手順:
- リリースビルドで実行
- ベンチマークで現状を測定
- プロファイリングで問題箇所を特定
- アルゴリズム改善またはコード最適化
- ベンチマークで改善を確認
繰り返し測定・改善することで、最適なコードが生まれます。
Footnotes
-
Donald Knuth の “Premature optimization is the root of all evil” より ↩