一、用優化(Release)構建
? 務必在做性能測量前使用 優化模式 構建你的 WASM。默認情況下:
wasm-pack build
→ Release + 優化wasm-pack build --dev
或cargo build
→ Debug,性能大打折扣
優化編譯能開啟 LLVM 的各項優化和 LTO,生成的機器碼才反映真實運行速度。否則,測得的時間只是一份 Debug 二進制在做「慢動作表演」。
二、性能計時:performance.now()
瀏覽器端最基本的高精度計時器就是 performance.now()
,它返回自頁面加載以來的毫秒浮點時間,精度可達微秒級。
2.1.Rust 調用示例
use wasm_bindgen::prelude::*;
use web_sys::window;fn now_ms() -> f64 {window().and_then(|w| w.performance()).map(|perf| perf.now()).expect("Performance should be available")
}#[wasm_bindgen]
pub fn heavy_compute(n: u32) -> f64 {let start = now_ms();let mut acc = 0;for i in 0..n {acc = acc.wrapping_add((i as u64).wrapping_mul(31)) as u32;}let elapsed = now_ms() - start;// 返回耗時,用 JS 拿到并打印elapsed
}
- 優點:調用開銷極低,可在熱循環內多次采樣,追蹤細粒度性能。
- 注意:在 Release 模式下測量,保證測得的時間不被 Debug 或符號信息影響。
三、console.time
/ console.timeEnd
當你希望在控制臺直觀地看到某一步驟的耗時,console.time
系列 API 非常好用。它會在 Console 中打印類似:
some task: 12.34ms
3.1.Rust 調用示例
use wasm_bindgen::prelude::*;
use web_sys::console;#[wasm_bindgen]
pub fn profile_task(n: u32) {console::time_with_label("compute"); // 標記開始let mut acc = 0;for i in 0..n {acc = acc.wrapping_mul(31).wrapping_add(i);}console::time_end_with_label("compute"); // 標記結束并輸出
}
在瀏覽器 DevTools 的 Console 面板,你會看到:
compute: 5.67ms
同時,這些日志也會出現在 Performance Timeline(瀑布流/水波圖)中,幫助你對齊其他事件。
四、瀏覽器 Profiler(火焰圖 & 調用樹)
現代瀏覽器(Chrome、Firefox、Edge)內建的 性能分析工具,可以錄制一段時間范圍內的所有 JS + WASM 調用,生成:
- 火焰圖(Flame Chart):以時間為橫軸,調用棧深度為縱軸,高亮最耗時函數。
- 調用樹(Call Tree):聚合各函數調用總時長,便于定位熱點。
4.1.使用建議
- 上傳 Release 并帶符號
在Cargo.toml
中確保profile.release.debug = true
,使瀏覽器能顯示 Rust 函數名,而非wasm-function[123]
。 - 短錄制
只選取熱點操作的時段(幾百 ms),太長會生成巨量數據。 - 看 Inlined 函數
由于 Rust/LLVM 廣泛自動內聯,Profiler 可能無法拆分內聯邏輯,只能看到調用者“成片”耗時。
五、#[bench]
與 Native Profilers
在投入大量時間調優之前,先確認瓶頸確實在WASM,而不是 JS 或 DOM。最直接的方式是:
-
本地寫
#[bench]
- 在
benches/
目錄下創建基準測試。 cargo bench
用系統最成熟的分析工具測出函數耗時。
- 在
-
使用 OS Profiler
- Linux 下的
perf
、macOS 下的 Instruments、Windows 下的 Windows Performance Recorder。 - 用它們分析本地
rlib
中的函數執行成本。
- Linux 下的
警告:千萬不要在未確認熱點在 WASM 側時,就開始針對 Release WASM 進行繁重優化——可能前端異步邏輯、內存分配或網絡才是瓶頸。
六、測一段計算密集型函數
-
Rust 代碼(
src/lib.rs
):use wasm_bindgen::prelude::*; use web_sys::console;fn now() -> f64 {web_sys::window().unwrap().performance().unwrap().now() }#[wasm_bindgen] pub fn crunch(n: u32) {let t0 = now();let mut x = 1u64;for i in 0..n {x = x.wrapping_mul(6364136223846793).wrapping_add(1);}let t1 = now();console::log_1(&format!("crunch({}) = {} in {:.2} ms", n, x, t1 - t0).into()); }
-
構建:
wasm-pack build --release
-
在頁面調用:
<script type="module">import init, { crunch } from "./pkg/my_wasm.js";async function run() {await init();crunch(10_000_000);}run(); </script>
-
查看結果
Console 中將打印:crunch(10000000) = 1234567890123456 in 45.67 ms
隨后可在 Profiler 中重點關注該函數位置。
七、性能剖析最佳實踐小結
- 始終用 Release + debug 符號 構建,保證真實性能與可讀堆棧。
- 從粗到細:先用瀏覽器 Profiler 定位大熱點,再用
performance.now()
或console.time
做微調。 - 本地測試優先:若可在純 Rust 單元測試中復現,優先用
#[bench]
+ OS 工具剖析。 - 控制樣本長度:錄制短時段、少量迭代,避免數據量過大。
- 關注內聯:內聯會讓火焰圖“融合”,必要時在 Rust 側加
#[inline(never)]
強制不內聯。 - 對比優化前后:每次做修改后都要重新構建并測量,避免“盲目優化”。
通過以上方法,你可以在 Rust→WASM 的代碼中快速發現并修復性能瓶頸,確保在瀏覽器端獲得最佳的吞吐與最低的延遲。祝你的 WebAssembly 應用越來越飛快!