前言

公众号:【可乐前端】,期待关注交换,分享一些有意思的前端常识

之前在一个本人的我的项目中尝试做一个web视频转码性能,打算用的是ffmpeg这个弱小的库。过后就理解到了wasmffmpeg移植到浏览器中应用。然而等真正要公布到生产的时候还是遇到一些问题,

比如说ffmpeg体积比拟大,加载速度迟缓;还有sharedArrayBufferffmpeg.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提供了靠近底层语言(如CC++)的性能,同时放弃了高级语言的形象个性。零老本形象的设计意味着你能够高效地管制硬件,而不会损失性能。
  • 并发性: 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文件,利用rustmd5库来计算文件的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-wasmvite-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计算MD5Rust计算MD5的速度进行了比照,我的测试笔记本是Apple M1芯片,8G内存。

后果如下,单位为秒

文件大小300K1.5M15M125M2G
Rust0.00360.00400.0320.26355.28
Js0.01240.0280.161.2621.148

从下面能够看出,Rust无论在任何文件体积下,速度都比JS5倍左右,看到这个后果我不禁感叹,Rust竟恐怖如斯。

最初

本文以计算MD5为场景,介绍了Rust打包Wasm产物并引入到Vite中应用的一种形式,纯属抛砖引玉。如果你有其余想法,欢送评论区或私信交换,如果感觉乏味的话,点点关注点点赞吧~

举荐浏览

  • Electron打造你本人的录屏软件
  • ️Electron实现录屏软件(二)——指定区域录制
  • 龙年大吉——AIGC生成龙年春联
  • 前端应用Lottie实现炫酷的开关成果
  • React+Node全栈无死角解析,吃透文件上传的各个场景