悬赏已过期 后悬赏过期

wasm真的比js快吗?

邀请:

一. 前言 首先提一句话,本人是Rust新手!如果有什么不足的地方麻烦指出哈! 最近一直在玩Rust(摸鱼),本来是想着,多学一点,多练一练,之后把我们这边的一些可视化项目里面核心的代码用Rust重构一下。但是我最近在练习一个demo的时候,发现了跟我预期不一样的地方。 具体如何,我用下面几个案例展开细说。 二. 案例1: 使用canvas绘制十万个不同颜色的圆 首先我想到的是,把canvas的复杂图像绘制功能用Rust重写一下。这里我用canvas绘制大量的圆形为例子。 2.1 Rust绘制canvas 跟上一篇文章的流程差不多,我们需要先新建一个Rust项目: cargo new canvas_circel_random –lib 然后更新一下Cargo.toml文件里面的依赖内容: [package] name = "canvas_circle_random" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib"] [dependencies] wasm-bindgen = "0.2" js-sys = "0.3" web-sys = { version = "0.3", features = ["HtmlCanvasElement", "CanvasRenderingContext2d"] } 完成之后,我们简单在src/lib.rs写一点代码: // 引入相关的依赖 use wasm_bindgen::prelude::*; use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement}; use js_sys::Math; // 给js调用的方法 #[wasm_bindgen] pub fn draw_circles(canvas: HtmlCanvasElement) { // 获取ctx绘画上下文 let context = canvas.get_context("2d").unwrap().unwrap().dyn_into::<CanvasRenderingContext2d>().unwrap(); let width = canvas.client_width() as f64; let height = canvas.client_height() as f64; // 循环绘制 for _ in 0..100_0000 { // 设置一下写x,y的位置 let x = Math::random() * width; let y = Math::random() * height; let radius = 10.0; let color = format!( "rgba({}, {}, {}, {})", (Math::random() * 255.0) as u8, (Math::random() * 255.0) as u8, (Math::random() * 255.0) as u8, Math::random() ); draw_circle(&context, x, y, radius, &color); } } fn draw_circle(context: &CanvasRenderingContext2d, x: f64, y: f64, radius: f64, color: &str) { // 调用canvas的API绘制 context.begin_path(); context.arc(x, y, radius, 0.0, 2.0 * std::f64::consts::PI).unwrap(); context.set_fill_style(&JsValue::from_str(color)); context.fill(); context.stroke();“` } 简单解释一下代码: 0..100_0000 创建了一个从 0 开始到 999,999 结束的范围注意,Rust 的范围是左闭右开的,这意味着它包含起始值但不包含结束值。 &JsValue::from_str(color)从变量中取值。 完成之后,我们去打包一下。 wasm-pack build –target web 然后我们得到一个pkg包,如下图: 然后我们在项目中引入一下,具体流程可以看我上一篇文章。 回到我们的Vue项目中,我们引入一下: import init, { draw_circles } from 'canvas_circle_random/canvas_circle_random' onMounted(async () => { await init(); const begin = new Date().getTime(); drawWasmCircle(); const end = new Date().getTime(); console.log('wasm cost time: ' + (end – begin) + 'ms'); }) 之后我们打开一下页面: 多次加载了几次,加载范围大概在2750ms~2900ms之间。 2.2 使用js绘制canvas const drawJsCircle = () => { const canvas = document.getElementById('my-canvas') as HTMLCanvasElement; const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; for (let i = 0; i < 1000000; i++) { drawRandomCircle(ctx, 800, 600); } } const drawRandomCircle = (ctx: CanvasRenderingContext2D, width: number, height: number) => { const radius = 10; const x = Math.random() * (width – 2 * radius) + radius; const y = Math.random() * (height – 2 * radius) + radius; const color = `rgba(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.random().toFixed(2)})`; ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2); ctx.fillStyle = color; ctx.fill(); ctx.stroke(); } 没什么好说的,有手就会。 然后我们在页面上试一下: 加载范围大概在1950ms~2200ms之间。 卧槽,难道说js的性能比wasm快??? 然后我又对绘制的数量和绘制的形状做了多次实验: 绘制10000个圆, wasm用时大概在1000ms,js用时大概在700ms。 绘制100000个长方形,wasm用时大概在1700ms, js用时在1100ms。 无一例外,在绘制canvas上面,js的性能确实优于wasm。 <顺便吆喝一句,民族企业核心岗,前、后端/测试,北京、东莞、西安、苏州、上海、武汉等多地捞人。给的还可以哦~> 三. 案例2:算法性能 考虑到通过canvas绘制图形来判断性能,有点太前端化了,我想可不可以通过写一些算法来做一下性能的比较。 试了很多算法,这里我用一下斐波那契算法,比较简单也比较有代表性。 在同级目录下新建一个Rust项目: cargo new fb-lib –lib 然后在fb-lib中修改一下Cargo.toml: [package] name = "fb-lib" version = "0.1.0" edition = "2021" [lib] crate-type = ["cdylib", "rlib"] [dependencies] wasm-bindgen = "0.2" 把斐波那契数列代码写到src/lib.rs文件中: use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn fb_wasm(n: i32) -> i32 { if n <= 1 { 1 } else { fb_wasm(n – 1) + fb_wasm(n – 2) } } 很简单,没什么好说的。完成之后,我们在项目中使用一下。 <script setup lang="ts"> import init, { fb_wasm } from 'fb-lib/fb_lib' import { onMounted } from 'vue'; onMounted(async () => { await init(); const begin = new Date().getTime(); fb_wasm(42); const end = new Date().getTime(); console.log('wasm cost time: ' + (end – begin) + 'ms'); }) </script> 大概试了一下时间在1700ms~1850ms左右。 然后我们用js实现一下:代码如下: import init, { fb_wasm } from 'fb-lib/fb_lib' import { onMounted } from 'vue'; onMounted(async () => { await init(); const begin = new Date().getTime(); fn_js(42); const end = new Date().getTime(); console.log('js cost time: ' + (end – begin) + 'ms'); }) const fn_js = (n: number): number => { if (n <= 1) { return 1; } else { return fn_js(n – 1) + fn_js(n – 2); } } 然后我们在页面上看一下: 大概试了一下时间在2550ms~2700ms左右。 很明显,这时的wasm的性能是要优秀于js。 四. 总结 大概试了一下canvas,dom操作,高性能算法(排序、递归)等。我大概得出了一个这样的结论: Wasm代码比JavaScript代码更加精简,因此从网络上获取Wasm代码的速度更快。 对于一些高性能的算法,在基数足够大的情况下,wasm的性能确实高于js,但是当基数比较小的时候,两者其实差不多。 由于Wasm是一种二进制格式,需要将DOM操作的数据进行转换,才能在Wasm和js之间进行传递。这个数据转换过程可能导致额外的开销。以及 Wasm操作DOM时,需要通过js提供的API进行通信。每次调用js的API都会引入一定的开销,从而影响性能。所以在一些页面交互操作上,wasm的性能并不会优于js。 综上,个人觉得wasm和js之间是一种互相选择互相依靠的关系,并不是取代的关系。日常开发中,也要结合实际情况选择不同的方式进行开发。   ——转自作者:尝尝你的优乐美  出处:juejin.cn/post/7444450769488674825

您的回答

回答

默认排序 时间排序
图片审查中...
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索