wasm真的比js快吗?(wasm和js)
ztj100 2025-06-12 19:07 59 浏览 0 评论
一. 前言
首先提一句话,本人是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
然后我们在项目中引入一下,具体流程可以看我上一篇文章。 回到我们的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
[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>
大概试了一下时间在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之间是一种互相选择互相依靠的关系,并不是取代的关系。日常开发中,也要结合实际情况选择不同的方式进行开发。
相关推荐
- Linux集群自动化监控系统Zabbix集群搭建到实战
-
自动化监控系统...
- systemd是什么如何使用_systemd/system
-
systemd是什么如何使用简介Systemd是一个在现代Linux发行版中广泛使用的系统和服务管理器。它负责启动系统并管理系统中运行的服务和进程。使用管理服务systemd可以用来启动、停止、...
- Linux服务器日常巡检脚本分享_linux服务器监控脚本
-
Linux系统日常巡检脚本,巡检内容包含了,磁盘,...
- 7,MySQL管理员用户管理_mysql 管理员用户
-
一、首次设置密码1.初始化时设置(推荐)mysqld--initialize--user=mysql--datadir=/data/3306/data--basedir=/usr/local...
- Python数据库编程教程:第 1 章 数据库基础与 Python 连接入门
-
1.1数据库的核心概念在开始Python数据库编程之前,我们需要先理解几个核心概念。数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,它就像一个电子化的文件柜,能让我们高效...
- Linux自定义开机自启动服务脚本_linux添加开机自启动脚本
-
设置WGCloud开机自动启动服务init.d目录下新建脚本在/etc/rc.d/init.d新建启动脚本wgcloudstart.sh,内容如下...
- linux系统启动流程和服务管理,带你进去系统的世界
-
Linux启动流程Rhel6启动过程:开机自检bios-->MBR引导-->GRUB菜单-->加载内核-->init进程初始化Rhel7启动过程:开机自检BIOS-->M...
- CentOS7系统如何修改主机名_centos更改主机名称
-
请关注本头条号,每天坚持更新原创干货技术文章。如需学习视频,请在微信搜索公众号“智传网优”直接开始自助视频学习1.前言本文将讲解CentOS7系统如何修改主机名。...
- 前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令
-
在Linux服务器管理中,SSH(SecureShell)是远程操作的核心工具。以下是SSH终端操作的常用命令和技巧,涵盖连接、文件操作、系统管理等场景:一、SSH连接服务器1.基本连接...
- Linux开机自启服务完全指南:3步搞定系统服务管理器配置
-
为什么需要配置开机自启?想象一下:电商服务器重启后,MySQL和Nginx没自动启动,整个网站瘫痪!这就是为什么开机自启是Linux运维的必备技能。自启服务能确保核心程序在系统启动时自动运行,避免人工...
- Kubernetes 高可用(HA)集群部署指南
-
Kubernetes高可用(HA)集群部署指南本指南涵盖从概念理解、架构选择,到kubeadm高可用部署、生产优化、监控备份和运维的全流程,适用于希望搭建稳定、生产级Kubernetes集群...
- Linux项目开发,你必须了解Systemd服务!
-
1.Systemd简介...
- Linux系统systemd服务管理工具使用技巧
-
简介:在Linux系统里,systemd就像是所有进程的“源头”,它可是系统中PID值为1的进程哟。systemd其实是一堆工具的组合,它的作用可不止是启动操作系统这么简单,像后台服务...
- Linux下NetworkManager和network的和平共处
-
简介我们在使用CentoOS系统时偶尔会遇到配置都正确但network启动不了的问题,这问题经常是由NetworkManager引起的,关闭NetworkManage并取消开机启动network就能正...
你 发表评论:
欢迎- 一周热门
-
-
MySQL中这14个小玩意,让人眼前一亮!
-
旗舰机新标杆 OPPO Find X2系列正式发布 售价5499元起
-
面试官:使用int类型做加减操作,是线程安全吗
-
C++编程知识:ToString()字符串转换你用正确了吗?
-
【Spring Boot】WebSocket 的 6 种集成方式
-
PyTorch 深度学习实战(26):多目标强化学习Multi-Objective RL
-
pytorch中的 scatter_()函数使用和详解
-
与 Java 17 相比,Java 21 究竟有多快?
-
基于TensorRT_LLM的大模型推理加速与OpenAI兼容服务优化
-
这一次,彻底搞懂Java并发包中的Atomic原子类
-
- 最近发表
-
- Linux集群自动化监控系统Zabbix集群搭建到实战
- systemd是什么如何使用_systemd/system
- Linux服务器日常巡检脚本分享_linux服务器监控脚本
- 7,MySQL管理员用户管理_mysql 管理员用户
- Python数据库编程教程:第 1 章 数据库基础与 Python 连接入门
- Linux自定义开机自启动服务脚本_linux添加开机自启动脚本
- linux系统启动流程和服务管理,带你进去系统的世界
- CentOS7系统如何修改主机名_centos更改主机名称
- 前端工程师需要熟悉的Linux服务器(SSH 终端操作)指令
- Linux开机自启服务完全指南:3步搞定系统服务管理器配置
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)