| name | rust-performance |
| description | Rust 效能優化技能。涵蓋零成本抽象原理、iterator vs loop 效能比較、 SIMD hints(std::simd)、cargo bench + criterion 2.0 微基準測試、 flamegraph 效能分析、perf/DHAT 工具鏈、記憶體配置優化(arena allocator)、 String 與 &str 效能策略、HashMap hasher 選擇、編譯器優化等級(opt-level/LTO/codegen-units)、 async overhead 與零成本取消、#[inline] 策略。 觸發關鍵詞:Rust performance, benchmark, criterion, flamegraph, profiling, zero-cost abstraction, iterator performance, SIMD, LTO, opt-level, codegen-units, arena allocator, perf, DHAT, inline, cache friendly
|
Rust 效能優化
適用場景
- 需要對 Rust 程式進行效能分析與瓶頸定位
- 使用 criterion 撰寫微基準測試,量化優化效果
- 選擇正確的編譯器優化選項(opt-level / LTO / codegen-units)
- 理解零成本抽象原理,避免不必要的效能損失
- 處理大量資料時選擇 iterator chain 或手動迴圈
- 使用 flamegraph / perf / DHAT 進行 CPU 與記憶體分析
- 優化記憶體配置策略(arena allocator、預分配)
- 選擇適當的 HashMap hasher 或容器類型
- 判斷
#[inline] 的使用時機
核心知識
效能優化優先順序
演算法複雜度 > 資料結構選擇 > 系統層優化 > 微優化
O(n²→n) Vec vs LinkedList I/O batching #[inline]
原則:先 profile,再優化。永遠以量測結果驗證假設。
Cargo Profile 設定
[profile.dev]
opt-level = 0
debug = true
[profile.release]
opt-level = 3
lto = "fat"
codegen-units = 1
panic = "abort"
strip = "symbols"
[profile.bench]
inherits = "release"
debug = true
[profile.dev-fast]
inherits = "dev"
opt-level = 1
編譯優化選項表
| 選項 | 值 | 效果 | 適用場景 |
|---|
opt-level | 0 | 無優化,最快編譯 | 開發 |
opt-level | 1 | 基本優化 | 開發 + 需要合理效能 |
opt-level | 2 | 大多數優化 | 一般 release |
opt-level | 3 | 含向量化在內的全部優化 | 效能關鍵 release |
opt-level | "s" | 優化體積 | 嵌入式 / WASM |
opt-level | "z" | 最小體積 | 極度受限環境 |
lto | "thin" | 快速 LTO | 日常 release |
lto | "fat" | 完整 LTO(最慢編譯) | 最終部署 |
codegen-units | 1 | 單一 unit,最大優化 | 最終部署 |
codegen-units | 16 | 預設,平行編譯 | 開發 |
strip | "symbols" | 移除符號表 | 部署二進位 |
panic | "abort" | 移除 unwind 表 | 不需 catch_unwind |
零成本抽象原理
Rust 的核心承諾:高階抽象在編譯後產生與手寫低階等價的機器碼。
let sum: i64 = (0..1000).filter(|x| x % 2 == 0).sum();
let mut sum: i64 = 0;
for i in 0..1000 {
if i % 2 == 0 { sum += i; }
}
關鍵機制:
- 單態化(Monomorphization):泛型在編譯期展開為具體類型
- 內聯(Inlining):小函式在呼叫處展開
- 迭代器融合(Iterator fusion):多個
.map().filter() 合併為單一迴圈
Iterator vs Loop 效能比較
| 面向 | Iterator Chain | 手動 Loop |
|---|
| 自動向量化 | 編譯器更容易推導 | 需手動確保結構 |
| 邊界檢查 | 通常被消除 | 需用 get_unchecked 手動消除 |
| 可讀性 | 宣告式、鏈式 | 命令式 |
| 效能 | release 模式通常等價或更快 | debug 模式通常更快 |
建議:優先使用 iterator chain,僅在 benchmark 證實有差異時才改用手動迴圈。
#[inline] 策略
#[inline]
pub fn is_power_of_two(n: u64) -> bool {
n != 0 && (n & (n - 1)) == 0
}
#[inline(always)]
fn hot_path_tiny_fn(x: u32) -> u32 { x.wrapping_mul(2654435761) }
#[inline(never)]
fn expensive_operation(data: &[u8]) -> Vec<u8> { }
使用準則:
- 同一 crate 內:編譯器通常自行決定,不需標記
- 跨 crate 的小型 pub 函式:使用
#[inline]
- 泛型函式:已經單態化,不需標記
#[inline(always)]:僅在 benchmark 證實有效時使用
String 與 &str 效能策略
fn greet(name: &str) -> String {
format!("Hello, {name}!")
}
use std::borrow::Cow;
fn normalize(input: &str) -> Cow<'_, str> {
if input.contains('\t') {
Cow::Owned(input.replace('\t', " "))
} else {
Cow::Borrowed(input)
}
}
let mut s = String::with_capacity(1024);
for item in items {
s.push_str(&item.to_string());
}
HashMap Hasher 選擇
use std::collections::HashMap;
let map: HashMap<String, i32> = HashMap::new();
use std::hash::BuildHasherDefault;
use ahash::AHasher;
type FastMap<K, V> = HashMap<K, V, BuildHasherDefault<AHasher>>;
let map: FastMap<u64, String> = FastMap::default();
use rustc_hash::FxHashMap;
let map: FxHashMap<u64, Vec<u8>> = FxHashMap::default();
| Hasher | 速度 | DoS 安全 | 適用場景 |
|---|
| SipHash(預設) | 中等 | 是 | 接受外部輸入 |
| AHash | 快 | 是 | 通用高效能 |
| FxHash | 極快 | 否 | 編譯器內部、整數 key |
Arena Allocator
use bumpalo::Bump;
let arena = Bump::new();
let s = arena.alloc_str("hello");
let v = arena.alloc_slice_copy(&[1, 2, 3, 4, 5]);
Profiling 工具鏈
| 工具 | 用途 | 平台 | 安裝 |
|---|
cargo bench + criterion | 微基準測試 | 全平台 | Cargo 依賴 |
cargo flamegraph | CPU 熱點視覺化 | Linux/macOS | cargo install flamegraph |
perf | Linux CPU profiling | Linux | 系統工具 |
| DHAT | 堆記憶體分析 | 全平台 | Cargo 依賴 |
| Instruments | macOS 全方位分析 | macOS | Xcode 內建 |
cargo-show-asm | 檢視生成組合語言 | 全平台 | cargo install cargo-show-asm |
| Cachegrind | 快取命中分析 | Linux | valgrind 套件 |
常見效能陷阱
- Debug 模式下做效能評估:debug 無優化,iterator 遠慢於 loop
- 不必要的
.clone():複製大型結構體造成配置壓力
- 過度 Boxing:
Box<dyn Trait> 引入間接呼叫 + 堆配置
- HashMap 碰撞:大量相似 key 導致退化
- 無界配置:
Vec 反覆 grow 造成多次 realloc
- 過度使用
String:該用 &str 時用了 String
- 忽略 cache locality:AoS vs SoA 資料佈局差異
程式碼範例
Basic: Iterator / Inline / String 優化
Intermediate: Criterion 基準測試
Advanced: Flamegraph + Arena + SIMD 佈局
常見錯誤對照表
| 錯誤訊息 / 現象 | 原因 | 修復方式 |
|---|
| Debug 模式效能僅為 release 的 1/10~1/50 | opt-level=0 無內聯、無向量化 | 使用 cargo build --release 或 [profile.dev] opt-level=1 |
| 函式呼叫佔據 flamegraph 大量比例但邏輯簡單 | 未內聯的小函式 | 跨 crate 函式加 #[inline];確認 LTO 設定 |
.clone() 出現在 profiling 熱點 | 不必要的深拷貝 | 改用借用 &T、Cow<T> 或 Arc<T> |
Box<dyn Trait> 虛擬呼叫成本高 | 動態分派 + 堆配置 | 改用 enum dispatch 或泛型 + 單態化 |
| HashMap 查找時間異常增長 | hash 碰撞嚴重或負載因子過高 | 換用 ahash/FxHash;呼叫 reserve() 預配置 |
Vec 多次 realloc 導致效能低落 | 未預分配容量 | 使用 Vec::with_capacity(n) 預分配 |
| criterion 報告 "regression detected" | 基準測試不穩定或環境干擾 | 關閉 CPU turbo boost;增加 warm-up time;使用 --baseline |
| 記憶體使用量持續增長 | 大量小物件反覆 alloc/dealloc | 使用 arena allocator(bumpalo)批量管理 |
| SIMD 未觸發自動向量化 | 迴圈結構不規則或有分支 | 使用 cargo-show-asm 確認;重構為線性存取模式 |
| async 任務 overhead 過高 | 過多 spawn + 過小任務粒度 | 合併小任務;使用 FuturesUnordered 批次處理 |
Cargo.toml 依賴模板
[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
dhat = "0.3"
[dependencies]
bumpalo = { version = "3.16", features = ["collections"] }
ahash = "0.8"
rustc-hash = "2.1"
[[bench]]
name = "my_benchmark"
harness = false
Flamegraph 工具安裝
cargo install flamegraph
cargo install cargo-show-asm
echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
參考來源
詳細來源清單見 references/sources.md。