前言
公众号:【可乐前端】,期待关注交换,分享一些有意思的前端常识
之前在一个本人的我的项目中尝试做一个web
视频转码性能,打算用的是ffmpeg
这个弱小的库。过后就理解到了wasm
把ffmpeg
移植到浏览器中应用。然而等真正要公布到生产的时候还是遇到一些问题,
比如说ffmpeg
体积比拟大,加载速度迟缓;还有sharedArrayBuffer
与ffmpeg.wasm
的一些关系,简略来说就是如果须要应用多线程版的ffmpeg
,就须要设置COOP/COEP
这两个新的跨域策略,然而设置这两个货色就会毁坏OAuth
的集成;或者能够抉择应用单线程的ffmpeg
,然而效率感人。。
然而这并不影响将C/C++/Rust
等语言编译成Wasm
移植到浏览器仍旧是一种很有魅力的解决方案,明天一起来走进它吧!
Wasm简介
WebAssembly(Wasm)
是一种凋谢规范,旨在提供一种可移植、高性能的二进制格局,用于在web
浏览器中运行。它不是特定于任何语言的,而是为多种编程语言设计,包含C、C++、Rust
等。通过将代码编译为Wasm
格局,开发人员能够实现在不同平台和浏览器上运行的一致性性能。
Wasm
的次要指标之一是提供比传统的JavaScript
更高效的执行速度。它容许开发人员应用其余语言编写局部应用程序,而后将这些局部集成到web
应用程序中,实现更好的性能和更宽泛的语言选择。
此外,Wasm
还提供了安全性、可移植性和版本控制等方面的劣势。它在web
浏览器中作为一个虚拟机执行,与浏览器的JavaScript
引擎严密集成,使得web
应用程序能够更高效地利用底层硬件资源。
Hello Rust
Rust
是一种零碎级编程语言,重视内存平安、并发性和性能。由Mozilla
开发,应用它能够高效地管制硬件,同时放弃高级语言的安全性。具体有以下比较突出的特点:
- 内存平安:
Rust
通过所有权零碎、生命周期检查和借用机制,无效地避免了空指针援用、数据竞争和内存透露等内存平安问题,使得编写平安的并发代码更为容易。 - 性能:
Rust
提供了靠近底层语言(如C
和C++
)的性能,同时放弃了高级语言的形象个性。零老本形象的设计意味着你能够高效地管制硬件,而不会损失性能。 - 并发性:
Rust
通过所有权零碎和借用机制,反对并发编程,同时防止了常见的并发谬误。这使得开发者可能编写线程平安的代码,而不须要额定的锁或同步原语。 - 生态系统:
Rust
领有一个一直壮大的生态系统,有丰盛的库和工具,涵盖了各种利用场景。这使得开发者可能更容易地构建各种类型的利用,从零碎级利用到Web
服务。 - 开发者敌对:
Rust
的语法清晰、现代化,领有敌对的文档和社区反对。它激励编写易读易保护的代码,同时提供了丰盛的工具链和调试反对。
Rust
的具体装置形式能够参考这个文档:Rust装置,如果你是Mac
用户,看到下图的时候示意Rust曾经装置结束:
装置Rust
的时候个别状况下会自带装置cargo,它是Rust
的库管理工具,相似于npm
。咱们能够应用 cargo new hello_rust
来创立一个Rust
我的项目。
我的项目装置好之后构造目录大抵如上,如果你是应用vscode
进行开发的话,倡议装置rust-analyzer
这个插件,它提供了代码的语法分析、主动实现、谬误剖析等性能,能够大大的晋升咱们的开发效率。
上面执行一下cargo run
命令,就能够把咱们的Rust
我的项目跑起来:
计算文件MD5
首先应用cargo new
来创立一个Rust
我的项目,在Cargo.toml
中填入以下的内容
[package]name = "rust_md5"version = "0.1.0"edition = "2021"[lib]crate-type = ["cdylib"][dependencies]md-5 = "0.9.0"js-sys = "0.3.50"wasm-bindgen = "0.2.73"[profile.release]opt-level = 3lto = truestrip = truepanic = "abort"
解释一下下面的字段:
package
:包的相干信息name
:指定了你的我的项目的名称。每个 Rust 我的项目都有一个惟一的名称。version
:指定了你的我的项目的版本号。这遵循语义化版本标准(Semantic Versioning),通常包含主版本号、次版本号和订正号。edition
:指定了 Rust 编译器所应用的语言版本。在这里,它指定了我的项目应用 Rust 2021 Edition。
lib
crate-type
:这里指定了生成的 crate 的类型为动态链接库(.cdylib
),这通常用于构建 WebAssembly 模块。
dependencies
依赖项[profile.release]
:对于 release 模式的配置。opt-level = 3
:指定了编译器的优化级别。在 release 模式下,通常抉择最高级别(3),以便进行更弱小的优化。lto = true
:启用 Link Time Optimization(LTO),这容许在链接阶段进行更宽泛的优化。strip = true
:启用在编译完结后去除调试信息和未应用的代码等优化,以减小生成的二进制文件的大小。panic = "abort"
:指定了在 release 模式下产生 panic 时的解决形式。这里设置为 "abort",示意在产生 panic 时立刻终止程序。
而后在src
文件夹下新增一个lib.rs
文件,利用rust
的md5
库来计算文件的md5
,其中输出是uint8
数组,输入是一个字符串
use md5::{Digest, Md5};use wasm_bindgen::prelude::*;use wasm_bindgen::JsValue;#[wasm_bindgen]#[repr(C)]pub struct RustMd5 {}#[wasm_bindgen]impl RustMd5 { pub fn new() -> Self { RustMd5 { } } pub fn calculate_md5(&self, file_buffer: &[u8]) -> Result<String, JsValue> { let mut md5 = Md5::new(); md5.update(file_buffer); let result = md5.finalize(); let md5_string = result .iter() .map(|b| format!("{:02x}", b)) .collect::<String>(); Ok(md5_string) }}
打包Wasm
接下来咱们就能够把这个rust
工程来打包成wasm
产物,应用到的是wasm-pack
这个工具,首先能够应用cargo install wasm-pack
来装置这个打包工具,而后执行wasm-pack build
就能够开始打包。
打包进去的产物如下
Vite引入应用
打包好wasm
模块之后,咱们就能够将其引入到我的项目中应用了,这里我以vite
搭建的工程为例,介绍如何把wasm
模块引入到我的项目之中应用。Vite
的配置文件如下,重点须要关注的是vite-plugin-wasm
和vite-plugin-top-level-await
这两个包,记得提前装置好。
import { defineConfig } from "vite";import react from "@vitejs/plugin-react";import wasm from "vite-plugin-wasm";import topLevelAwait from "vite-plugin-top-level-await";export default defineConfig({ plugins: [ react(), wasm(), topLevelAwait() ],});
而后把打包好的pkg
文件夹放到前端我的项目下,用以下的形式引入:
import * as wasm from "./pkg/rust_md5.js";console.log(wasm);const rustMd5 = wasm.RustMd5.new();
接下来就能够上传文件并计算文件的MD5
了,这里须要留神的是咱们写的Rust
模块计算MD5
的办法承受的是一个Uint8Array
,所以前端须要转换一下再传输给Rust
,示例代码如下:
const handleFileChange = (e) => { const uploadedFile = e.target.files[0]; // 应用 FileReader 读取文件并转换为 ArrayBuffer const fileReader = new FileReader(); console.log("读取文件并转换为 ArrayBuffer"); fileReader.onload = function (e) { // 获取 ArrayBuffer const arrayBuffer = e.target.result; const startTime = performance.now(); const uint8Array = new Uint8Array(arrayBuffer); const res = rustMd5.calculate_md5(uint8Array); console.log("res", res); const endTime = performance.now(); const executionTime = (endTime - startTime) / 1000; // 单位:秒 alert(executionTime + "s"); }; // 以 ArrayBuffer 格局读取文件 fileReader.readAsArrayBuffer(uploadedFile); }; // 省略一些代码<input type="file" onChange={handleFileChange} />
比照JS
我应用了几种规格的文件大小,别离对JS
计算MD5
和Rust
计算MD5
的速度进行了比照,我的测试笔记本是Apple M1
芯片,8G
内存。
后果如下,单位为秒
文件大小 | 300K | 1.5M | 15M | 125M | 2G |
---|---|---|---|---|---|
Rust | 0.0036 | 0.0040 | 0.032 | 0.2635 | 5.28 |
Js | 0.0124 | 0.028 | 0.16 | 1.26 | 21.148 |
从下面能够看出,Rust
无论在任何文件体积下,速度都比JS
快5
倍左右,看到这个后果我不禁感叹,Rust
竟恐怖如斯。
最初
本文以计算MD5
为场景,介绍了Rust
打包Wasm
产物并引入到Vite
中应用的一种形式,纯属抛砖引玉。如果你有其余想法,欢送评论区或私信交换,如果感觉乏味的话,点点关注点点赞吧~
举荐浏览
- Electron打造你本人的录屏软件
- ️Electron实现录屏软件(二)——指定区域录制
- 龙年大吉——AIGC生成龙年春联
- 前端应用Lottie实现炫酷的开关成果
- React+Node全栈无死角解析,吃透文件上传的各个场景