あなたのRustは遅すぎる - 20の実践的な方法でコードを最適化する
Jul 19, 2025
# rust
Lukas Schneider
DevOps Engineer · Leapcell

Rustのパフォーマンス最適化のための20の実践的なヒント
Rustは、パフォーマンスに重点を置いたシステムプログラミング言語として、多くのシナリオで優れたパフォーマンスを発揮してきました。しかし、Rustのポテンシャルを最大限に引き出し、効率的なコードを書くためには、いくつかのパフォーマンス最適化技術を習得する必要があります。この記事では、Rustのパフォーマンス最適化のための20の実践的なヒントを、具体的なコード例を交えて紹介します。
- 適切なデータ構造を選択する
異なるデータ構造は異なるシナリオに適しており、正しく選択することでパフォーマンスを大幅に向上させることができます。例えば、コレクション内の要素を頻繁に挿入および削除する必要がある場合、
Vec
よりもVecDeque
がより適切かもしれません。高速なルックアップが必要な場合は、HashMap
またはBTreeMap
がより良い選択肢となります。
// VecDequeをキューとして使用する use std::collections::VecDeque; let mut queue = VecDeque::new(); queue.push_back(1); queue.push_back(2); let item = queue.pop_front(); // HashMapを高速ルックアップに使用する use std::collections::HashMap; let mut scores = HashMap::new(); scores.insert("Alice", 100); let score = scores.get("Alice");
- イテレータとクロージャを活用する Rustのイテレータとクロージャは、コレクションを効率的かつ簡潔に処理する方法を提供します。イテレータメソッドを連鎖させることで、中間変数を作成することを避け、不要なメモリ割り当てを削減できます。
let numbers = vec![1, 2, 3, 4, 5]; let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect(); let sum: i32 = doubled.iter().sum();
- 不要なメモリ割り当てを減らす
スタック割り当てはヒープ割り当てよりも高速であるため、ヒープ割り当てよりもスタック割り当てを優先します。固定サイズのデータ構造の場合、動的な
Vec
の代わりに配列を使用します。
// スタック割り当ての配列を使用する let arr: [i32; 5] = [1, 2, 3, 4, 5]; // Vecの動的な拡張を減らすために、事前に容量を割り当てる let mut vec = Vec::with_capacity(100); for i in 1..=100 { vec.push(i); }
String
の代わりに&str
を使用する 文字列を扱う場合、文字列を変更する必要がない場合は&str
を使用します。&str
は読み取り専用の参照であり、ヒープ割り当ては必要ありません。一方、String
は可変であり、ヒープ割り当てが必要です。
fn process(s: &str) { println!("Processing string: {}", s); } fn main() { let s1 = "Hello, Rust!"; // &str let s2 = String::from("Hello, Rust!"); // String process(s1); process(&s2); // ここでStringを&strに変換する }
- 不要なクローンとコピーを避ける クローンとコピーは、特に大きなデータ構造の場合、パフォーマンスのオーバーヘッドを引き起こす可能性があります。可能な場合は、クローンまたはコピーの代わりに参照でデータを渡します。
fn print_numbers(numbers: &[i32]) { for num in numbers { println!("{}", num); } } fn main() { let numbers = vec![1, 2, 3, 4, 5]; print_numbers(&numbers); // クローンを避けるために参照で渡す }
- ループを最適化する
不変の計算をループの外に移動することにより、ループ内の不要な操作を削減します。単純なループでは、余分なオーバーヘッドを避けるために
for
の代わりにwhile
を検討してください。
// 最適化前 let mut result = 0; for i in 1..=100 { let factor = 2 * i; result += factor; } // 最適化後 let factor = 2; let mut result = 0; for i in 1..=100 { result += factor * i; }
if let
とwhile let
で条件分岐を簡略化するif let
とwhile let
は冗長なmatch
式を減らし、コードをよりクリーンにし、パフォーマンスを向上させる可能性があります。
// if letでOptionの処理を簡略化する let value: Option<i32> = Some(42); if let Some(num) = value { println!("The value is: {}", num); } // while letでIteratorの処理を簡略化する let mut numbers = vec![1, 2, 3, 4, 5].into_iter(); while let Some(num) = numbers.next() { println!("{}", num); }
const
とstatic
を活用するconst
はコンパイル時に評価される定数を定義し、実行時のメモリを占有しません。static
はプログラム全体のライフサイクルにわたる変数を定義します。パフォーマンスを向上させるために、これらを適切に使用してください。
const PI: f64 = 3.141592653589793; fn calculate_area(radius: f64) -> f64 { PI * radius * radius } static COUNTER: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); fn increment_counter() { COUNTER.fetch_add(1, std::sync::atomic::Ordering::SeqCst); }
- コンパイラの最適化を有効にする
Cargo.toml
で、コンパイラの最適化を有効にするためにopt-level
を設定します。オプションには、0
(デフォルト、コンパイル時間を優先)、1
(基本的な最適化)、2
(より多くの最適化)、および3
(最大の最適化)があります。
[profile.release] opt-level = 3
- リンク時最適化(LTO)を使用する
LTOを使用すると、コンパイラはリンク中にプログラム全体を最適化し、パフォーマンスをさらに向上させることができます。
Cargo.toml
でLTOを有効にします。
[profile.release] lto = true
- 動的ディスパッチを削減する 動的ディスパッチ(例えば、トレイトオブジェクトを介してメソッドを呼び出す)は、メソッドルックアップのために実行時のオーバーヘッドが発生します。パフォーマンスが重要なコードでは、ジェネリクスを介した静的ディスパッチを優先します。
// 動的ディスパッチ trait Animal { fn speak(&self); } struct Dog; impl Animal for Dog { fn speak(&self) { println!("Woof!"); } } fn make_sound(animal: &dyn Animal) { animal.speak(); } // 静的ディスパッチ fn make_sound_static<T: Animal>(animal: &T) { animal.speak(); }
- 関数呼び出しを最適化する
小さな関数については、
#[inline]
属性を使用して、コンパイラにインライン化をヒントし、呼び出しのオーバーヘッドを削減します。
#[inline] fn add(a: i32, b: i32) -> i32 { a + b }
- クリティカルパスに
unsafe
コードを使用する パフォーマンスが重要なパスでunsafe
コードを慎重に使用して、Rustの安全チェックをバイパスしますが、コードの安全性を確保してください。
// 安全だが遅い実装 fn sum_safe(numbers: &[i32]) -> i32 { let mut sum = 0; for &num in numbers { sum += num; } sum } // 高パフォーマンスのunsafeな実装 fn sum_unsafe(numbers: &[i32]) -> i32 { let len = numbers.len(); let ptr = numbers.as_ptr(); let mut sum = 0; for i in 0..len { sum += unsafe { *ptr.add(i) }; } sum }
- 並列コンピューティングを活用する
Rustは、効率を向上させるためにマルチコアCPUを利用する
rayon
のような並列コンピューティングライブラリを提供します。
use rayon::prelude::*; let numbers = vec![1, 2, 3, 4, 5]; let doubled: Vec<i32> = numbers.par_iter().map(|x| x * 2).collect();
- データレイアウトを最適化する 適切なデータレイアウトはCPUキャッシュのヒット率を向上させます。関連するデータを連続したメモリに格納します。
// 良いデータレイアウト #[derive(Copy, Clone)] struct Point { x: i32, y: i32, } let points: Vec<Point> = vec![Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]; // 悪いデータレイアウト(仮説) struct SeparateData { x_values: Vec<i32>, y_values: Vec<i32>, }
- 時期尚早な最適化を避ける 最初は正確さと読みやすさを優先します。時期尚早な最適化はコードを複雑にし、最小限の改善しか得られない可能性があります。プロファイリングツールを使用して、最初にボトルネックを特定します。
// 単純だが潜在的に最適ではない実装 fn find_max(numbers: &[i32]) -> Option<i32> { let mut max = None; for &num in numbers { if max.is_none() || num > max.unwrap() { max = Some(num); } } max }
- SIMD命令を活用する
単一命令、複数データ(SIMD)命令は、複数のデータ要素を同時に操作し、数値計算のパフォーマンスを向上させます。Rustの
std::simd
モジュールはSIMDをサポートしています。
use std::simd::i32x4; let a = i32x4::new(1, 2, 3, 4); let b = i32x4::new(5, 6, 7, 8); let result = a + b;
- エラー処理を最適化する
効率的なエラー処理はオーバーヘッドを削減します。
Result
を使用する場合、通常実行パスでErr
値を作成することを避けてください。
// 最適化前 fn divide(a: i32, b: i32) -> Result<i32, String> { if b == 0 { return Err(String::from("Division by zero")); } Ok(a / b) } // 最適化後 fn divide(a: i32, b: i32) -> Result<i32, &'static str> { if b == 0 { return Err("Division by zero"); } Ok(a / b) }
- 頻繁に使用される結果をキャッシュする 同一の入力を持つ高価な関数の結果をキャッシュして、冗長な計算を避けます。
use std::collections::HashMap; fn expensive_computation(x: i32) -> i32 { // 高価な計算をシミュレートする std::thread::sleep(std::time::Duration::from_secs(1)); x * x } let mut cache = HashMap::new(); fn cached_computation(x: i32) -> i32 { if let Some(result) = cache.get(&x) { *result } else { let result = expensive_computation(x); cache.insert(x, result); result } }
- パフォーマンスプロファイリングツールを使用する
Rustエコシステムは、ベンチマーク用の
cargo bench
やプロファイリング用のperf
(Linux上)などのツールを提供します。これらは、対象を絞った最適化のためにボトルネックを特定します。
// cargo benchでベンチマークする #[cfg(test)] mod tests { use test::Bencher; #[bench] fn bench_function(b: &mut Bencher) { b.iter(|| { // テストするコード }); } }
これらの20のヒントを適用することで、Rustコードを効果的に最適化し、言語のパフォーマンス上の利点を活用して、効率的で信頼性の高いアプリケーションを構築できます。
Leapcell:最高のサーバーレスWebホスティング
最後に、Rustサービスをデプロイするための最高のプラットフォームをお勧めします:Leapcell
🚀 お気に入りの言語で構築する
JavaScript、Python、Go、またはRustで楽に開発します。
🌍 無制限のプロジェクトを無料でデプロイする
使用した分だけ支払います - リクエストも料金もありません。
⚡ 従量課金制、隠れたコストなし
アイドル料金なし、シームレスなスケーラビリティ。
🔹 Twitterでフォローしてください:@LeapcellHQ