wasm VS js,谁快?(一)反直觉
起因
WebAssembly(缩写wasm)是一种低级的类汇编语言,能够高效地运行在浏览器中,可由C、C++和Rust等语言编译生成。
好吧,看样子wasm像是比js要快,是真的吗?那不妨让我们对比下,真实地感受下wasm的性能,这里用rust编译生成wasm。
结论
风起喜欢用简单易懂地方式说明白问题,直接贴答案
bash
total nodes: 299593
wasm-deq-tree: 347.54443359375 ms
js-tree: 7232.81201171875 ms
我们用相同的算法构造一棵相同大小的树,看谁生成的快,这里可以看到这棵树的节点总数将近30万个。wasm需要约300毫秒完成,js需要约7秒完成,对比下来wasm比js快约20倍。
对比结束,不想看过程的朋友可以散了,剩下的朋友就和我一起踏上这段性能探索之旅吧。
尝试,反直觉
我们构造的树结构如下
bash
{
name: "node_0_0",
children: [
{
name: "node_1_0",
children: [...],
},
{
name: "node_1_1",
children: [...],
},
...
],
}
构造树的代码
rust代码
bash
#[wasm_bindgen]
#[derive(Clone)]
pub struct Item {
name: String,
children: Vec<Item>,
}
#[wasm_bindgen]
pub fn treeFun(treeLevel: u32, nodeWidth: u32) -> Item {
let tempNode = Item {
name: String::from(""),
children: vec![],
};
let mut root = tempNode.clone();
root.name = String::from("node_0_0");
if treeLevel < 2 {
return root;
}
let mut nodeStack: Vec<&mut [Item]> = vec![];
for m1 in 0..nodeWidth {
let mut curItem = tempNode.clone();
curItem.name = format!("node_{}_{}", 1, m1);
root.children.push(curItem);
}
let mut vs: Vec<&mut [Item]> = root.children.chunks_mut(1).collect();
for m2 in vs.iter_mut() {
nodeStack.push(*m2);
}
for i in 2..treeLevel {
let curTotal: u32 = nodeWidth.pow(i - 1);
for j in 0..curTotal {
let shiftItem = nodeStack.remove(0);
for k1 in 0..nodeWidth {
let mut curItem = tempNode.clone();
curItem.name = format!("node_{}_{}", i, nodeWidth * j + k1);
shiftItem[0].children.push(curItem);
}
let mut ss: Vec<&mut [Item]> = shiftItem[0].children.chunks_mut(1).collect();
for _ in 0..nodeWidth {
let curSs = ss.remove(0);
nodeStack.push(curSs);
}
}
}
root
}
js代码
bash
const treeFun = (treeLevel, nodeWidth) => {
const tempNode = {
name: '',
children: [],
};
const root = JSON.parse(JSON.stringify(tempNode));
root.name = 'node_0_0';
if (treeLevel < 2) {
return root;
}
const nodeStack = [];
for (var m1=0; m1<nodeWidth; m1++) {
const curItem = JSON.parse(JSON.stringify(tempNode));
curItem.name = `node_1_${m1}`;
root.children.push(curItem);
}
for (var m2=0; m2<nodeWidth; m2++) {
nodeStack.push(root.children[m2]);
}
for (var i=2; i<treeLevel; i++) {
const curTotal = Math.pow(nodeWidth, (i-1));
for (var j=0; j<curTotal; j++) {
const shiftItem = nodeStack.shift();
for (var k1=0; k1<nodeWidth; k1++) {
const curItem = JSON.parse(JSON.stringify(tempNode));
curItem.name = `node_${i}_${nodeWidth * j + k1}`;
shiftItem.children.push(curItem);
}
for (var k2=0; k2<nodeWidth; k2++) {
nodeStack.push(shiftItem.children[k2]);
}
}
}
return root;
}
我们尝试构造的树有7层深度,每个节点下有8个子节点,共299593个节点,执行结果如下
bash
total nodes: 299593
wasm-tree: 31134.56591796875 ms
js-tree: 7428.5888671875 ms
可以看到js执行速度明显快,奇怪了,反直觉,与预期不符。
分析rust代码,发现有这几部分:循环、对象深拷贝、向量切片、数学函数、向量的插入和删除。
我们对这几部分分别测试,看看到底哪里慢了。
欲知详情且听下回分解。