一、始终使用优化后的构建进行 Profiling
重要提醒: 当你想要测量 .wasm
的真实性能,需要使用 优化过的构建版本。
wasm-pack build
默认即为 release 配置,开启了编译优化。- 如果你使用
cargo build --release
,也会默认执行最高级别的 LLVM 优化。
在 debug 模式下,编译器会插入大量调试符号和额外检查,性能表现可能相差甚远,无法反映真实的执行效率。
二、使用 performance.now()
做简单直观的计时
最直接的方法之一就是依赖 浏览器提供的高精度计时 API:performance.now()
。它返回自页面加载以来的毫秒数,单次调用开销极小,不会显著干扰测量。
在 Rust 中使用:
rust
extern crate web_sys;
fn now() -> f64 {
web_sys::window()
.expect("no global `window` exists")
.performance()
.expect("should have `performance`")
.now()
}
fn expensive_operation() {
// 模拟某些耗时操作
}
#[wasm_bindgen]
pub fn measure_it() {
let start = now();
expensive_operation();
let end = now();
let duration = end - start;
web_sys::console::log_1(&format!("Operation took: {:.3} ms", duration).into());
}
你可以针对不同函数或逻辑片段打上 start
、end
记录,计算出每个阶段的时间差。
Tip:
performance.now()
常见应用场景包括:游戏/动画循环帧耗时、批量数据处理前后差值、网络请求前后耗时等。
三、使用浏览器内置的 Profiler 生成调用树或火焰图
1. 开启 Debug 符号
大多数现代浏览器(Chrome、Firefox、Edge 等)都自带 Profiler,会按照调用栈或火焰图可视化你代码执行过程中的耗时分布。但如果你的 .wasm
文件里没有保留调试符号 ,就只会显示诸如 wasm-function[42]
这样难以理解的名字。
解决: 确保启用 debug = true
或者使用 debug 构建,以在 .wasm
中保留自定义 name
section。
toml
[profile.release]
debug = true
2. 如何查看分析结果
打开浏览器的开发者工具(F12),切换到 Performance / Profiler / Memory 等面板,记录下整个执行过程后,即可在生成的火焰图或调用树里看到各函数用时。Rust 函数名会显示在面板中,可帮助你快速定位关键瓶颈。
注意: 由于 Rust/LLVM 广泛使用了函数内联 (inlining),有些函数可能不会单独显示在调用图中,这也许会让分析结果看起来有些"怪异"。
四、利用 console.time
和 console.timeEnd
标记时间区间
如果想要在 浏览器控制台 (以及性能分析的 timeline 中)快速看到特定区间的耗时,可使用 console.time
/ console.timeEnd
API。
用 web-sys
调用示例
rust
web_sys::console::time_with_label("my-operation");
// 这里放置你要执行的操作
// ...
web_sys::console::time_end_with_label("my-operation");
控制台将显示类似:
my-operation: 12.345ms
同时,这些标记也会出现在浏览器开发者工具的性能时间线中,直观体现操作耗时。
五、在原生环境下使用 #[bench]
进一步挖掘性能瓶颈
思路: 有些性能问题和算法复杂度,与 Web API 或 JS 交互无关。如果能先把此逻辑抽取出来,使用"原生" Rust 测试与基准工具,会更加高效。
-
在
Cargo.toml
的[lib]
中加入crate-type = ["cdylib", "rlib"]
。"cdylib"
用于生成.wasm
"rlib"
用于生成普通的 Rust 库
-
在
benches/
目录(或一个你指定的目录)编写基准测试文件。例如:rust// benches/my_bench.rs #![feature(test)] extern crate test; use test::Bencher; // 引入你的主库 extern crate my_lib; #[bench] fn bench_expensive_operation(b: &mut Bencher) { b.iter(|| { my_lib::expensive_operation(); }); }
-
执行基准测试:
bashcargo bench
-
然后可以利用操作系统下成熟的性能剖析工具(Linux 下
perf
, macOS 下Instruments.app
, Windows 下VTune
等)来对原生二进制进行更深层次的 Profiling。
这样做的前提是:你确定主要瓶颈在纯算法逻辑层,而不是浏览器或 JS 交互层。如果你误判,可能会在"并非瓶颈"的地方花费大量时间。
六、小结
- 先明确性能目标:吞吐量 (ops/s) 或延迟 (ms)?
- 编译优化:使用 Release 或最低体积、最高性能构建,避免 debug 模式干扰结果。
- 轻量计时 :
performance.now()
、console.time
配合控制台使用,快速锁定问题区间。 - 浏览器 Profiler:生成调用树或火焰图,定位最耗时的函数。
- 本地基准测试 :若逻辑可分离,用
#[bench]
+原生工具做深入分析。
通过这几个层次的 Profiling,既能看到宏观运行状况 ,也能定位微观算法瓶颈。当你一次次迭代后,再用同样方法回测指标,效果是否提升便一目了然。