关于前端:🚀JS使用Wasm为你的文件MD5计算装上火箭引擎🚀

前言

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

之前在一个本人的我的项目中尝试做一个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 = 3
lto = true
strip = true
panic = "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内存。

后果如下,单位为秒

文件大小 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无论在任何文件体积下,速度都比JS5倍左右,看到这个后果我不禁感叹,Rust竟恐怖如斯。

最初

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

举荐浏览

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

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理