风起提示:由于技术的发展风起云涌,需留意文章时效,2023-06-28。
回顾
开始新内容前让我们先回忆下之前的内容,之前我们构造了一棵约30万个节点的树,测出来结果 wasm比js慢约4倍,比较反直觉,所以我们拆分成:循环、对象深拷贝、向量切片、数学函数、向量的插入和删除,对这几部分分别测试,看看到底哪里慢了。
上一章我们发现wasm比js慢在向量的头部删除上,改用双端队列后性能大幅提升,这次我们重构下第一次尝试的代码看看效果。
重构
用双端队列及向量原始指针重构树的构建
rust代码
#[wasm_bindgen]
#[derive(Clone)]
pub struct Item {
name: String,
children: Vec<Item>,
}
#[wasm_bindgen]
pub fn unsafeTreeFun(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: VecDeque<&mut Item> = VecDeque::new();
for m1 in 0..nodeWidth {
let mut curItem = tempNode.clone();
curItem.name = format!("node_{}_{}", 1, m1);
root.children.push(curItem);
}
let ptr1 = root.children.as_mut_ptr();
unsafe {
for m2 in 0..nodeWidth {
nodeStack.push_back(&mut *ptr1.add(m2.try_into().unwrap()));
}
}
for i in 2..treeLevel {
let curTotal: u32 = nodeWidth.pow(i - 1);
for j in 0..curTotal {
let shiftItem = nodeStack.pop_front().unwrap();
for k1 in 0..nodeWidth {
let mut curItem = tempNode.clone();
curItem.name = format!("node_{}_{}", i, nodeWidth * j + k1);
shiftItem.children.push(curItem);
}
let ptr2 = shiftItem.children.as_mut_ptr();
unsafe {
for k2 in 0..nodeWidth {
nodeStack.push_back(&mut *ptr2.add(k2.try_into().unwrap()));
}
}
}
}
root
}
js代码参考第一次尝试里的,这里没有变。
结论
还是一个7层深度,每个节点下8个子节点,共299593个节点的树,执行结果
total nodes: 299593
wasm-deq-tree: 347.54443359375 ms
js-tree: 7232.81201171875 ms
最终可以看到wasm快的多,对于树的构建速度大概是js的20倍。
总结
这里测试只考虑wasm和js的执行速度,排除了wasm和js传递大量数据的场景,只是纯粹的体验下wasm的执行速度。
通过这个测试了解到,wasm从设计机制上确实比js运行得快,如果发现不快就要开始反思,是不是可以优化。
我们还发现js追求的是灵活方便,对底层数据结构做了很多封装和优化。rust追求的是性能稳定,提供了细粒度且严格的内存及数据结构控制。两种语言设计初衷不一样。
至于wasm和js的传参,需要了解wasm的内存模型,下次我们介绍。
更多精彩内容可关注风起的博客,微信公众号:听风说图