我目前在尝试用 Rust 编译成 webassembly 的方式来开发 web 应用的一部分(也有类似的框架比如 yew 等),但是我发现使用 webassembly 反而可能会有更多的消耗,例如我现在要做一个点击按钮调用 JS 函数进行一些计算,并且把结果以 dom 的方式呈现
只使用 js 的方案:
- JS 调用另外一个 JS 函数
- 另外一个 JS 函数进行一些计算,直接将结果通过 document 的一些 API 渲染上屏。
使用 webassembly 的方案:
- JS 调用一个 wasm 函数
- Wasm 进行一些计算,计算的过程中,将结果 encode 到一段 TypedArray 中,调用 JS 的函数渲染上屏
- JS 将 TypeArray 中对应的内容进行 decode,之后渲染上屏。
这里我们不考虑使用 React Vue 这些,这里看使用 WebAssembly 反而可能降低性能,这主要体现在:
- 更多的函数调用
- 增加了 encode/decode 过程,以及可能由此带来的内存拷贝。
Webassembly 的优点可能在于计算快一点,但是这个优点很可能因为上述增加的开销而变得不能节约整体的时间,很明显在涉及 DOM 操作的时候会变得很慢。
不过我仍然做了一个对比测试:
JS 创建 10000 个 p 标签,大约花费 120ms:
function web_bench() {
let container = document.getElementById("container");
let begin = Date.now();
for(let i = 0; i < 10000; i += 1) {
let str = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"123456789012345678901234567890123456abcd123456789012345678901234567890123456789012345678901234567890" +
"1234567890123456789012345678901234567890123456789012345678哈哈1234567890123456789012345678901234567890" +
"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"1234567890123456789012345678901234567890123456789哈哈2345678901234567890123456789012345678901234567890" +
"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" +
"123456789012345678901234567890123456789012345678901234567890123嘻嘻67890123456789012345678901234567890" +
"12345678901234567890123456789012345678901234哈哈哈012345678901234567890123456789012345678901234567890";
let p = document.createElement("p");
p.innerHTML = str;
container.appendChild(p);
}
let time = Date.now() - begin;
console.log('cost time:', time);
}
如果使用 Rust 来做:花费的时间在 180 ms,是 js 的 1.5 倍
#[wasm_bindgen]
pub fn bench() {
let document = web_sys::window().unwrap().document().unwrap();
for i in 0..10000 {
let str = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456abcd1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678哈哈123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789哈哈234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678902345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123嘻嘻6789012345678901234567890123456789012345678901234567890123456789012345678901234哈哈哈012345678901234567890123456789012345678901234567890";
let p = document
.create_element("p")
.unwrap();
p.set_inner_html(str);
document
.get_element_by_id("container")
.unwrap()
.append_child(&p);
}
}
因为我们做 web 开发总是绕不过 dom 的操作的,我们使用 rust 进行 web 开发的话,最终编译成 wasm 还是需要大量的 dom 操作。
这是不是说明,rust+wasm 并不适合做和 dom 操作交互比较频繁的 web 开发,只适合做计算辅助模块,目前的 Rust web 框架有是怎么看待这个问题的呢,我的分析是否正确呢?
欢迎提出相关意见~
评论区
写评论这种场景多抽象一层总归是慢的。好比后端不都在 bypass kernel 了吗?操作系统提供的抽象都恨不得不要了。阶段不同而已,前端受限于人的刷新速度,有 60 fps 就够了,再快没太大意义。如果有些地方慢一点,能加速后台的计算或者开发,那就是值得的。如果没有能加速的地方,那又何必用呢?
对以下内容的回复:
嗯嗯,谢谢,这里其实关注点在于:
https://rust.cc/article?id=e29030f4-539d-4d5d-bbf9-233c0eecf735 站内不是讨论过类似的东西吗