webAssembly学习及使用rust

news2025/1/16 13:56:22

学习理解 webAssembly 概念知识,使用 API 进行 web 前端开发。

概念

是一种运行在现代网络浏览器中的新型代码,并且提供新的性能特性和效果。它有一种紧凑的二进制格式,使其能够以接近原生性能的速度运行。C/C++、 C#、Rust等语言可以编译为 webAssembly 执行。

  • 快速、高效、可移植。不同平台中能够接近本地速度运行。
  • 可读、可调式。是一门低阶语言。
  • 保持安全。被限制运行在一个沙箱中,遵循浏览器的同源策略和授权策略。
  • 不破坏网络。向后兼容

以前无法以此方式运行的客户端软件都将可以运行在 Web 中。

WebAssembly 是一门不同于 JavaScript 的语言,它不是用来取代 JavaScript 的。相反,可以和 JavaScript 一起协同工作,从而使得网络开发者能够利用两种语言的优势

关键概念

  • 模块(module) - 表示一个已经被浏览器编译为可执行机器码的 WebAssembly 二进制代码
  • 内存(memory) - ArrayBuffer,大小可变。
  • 表格(table) - 带类型数组,大小可变
  • 实例(instance) - 一个模块及其在运行时使用的所有状态,包括内存、表格和一系列导入值

js 可以控制 WebAssembly 代码如何下载、编译运行。所以前端可以将 WebAssembly 作为一种高性能函数进行调用。

编写代码并应用

  • 构建完整基于 rust 的 web 应用,比如使用yew
  • 构建部分应用功能,比如提供一些方法,用于复杂的计算。

使用wasm-pack生成 webAssembly 代码

rust 环境参考另一篇文章rust 基础知识

wasm-pack可以构建生成与 js、浏览器、node 进行互操作的代码。可以将这些功能包发布到 npm 上。

安装,等待安装完成,可以通过wasm-pack -V查看版本,是否安装成功

wasm-pack

$> cargo install wasm-pack

创建一个 rust 项目js-utils,初始化为一个 lib 库cargo init --lib

配置Cargo.toml,添加wasm-binggen作为依赖,它是 wasm 与 js 之间交互的工具;添加crate-type设置为cdylib,指定该库是一个 C 兼容的动态库,这是针对 rust 编译时设置的一个标志。

添加依赖可以使用命令cargo add,在package字段中添加一些仓库、个人信息,比如authors/description/license/repository,在打包编译时,会被添加到package.json文件中

[package]
name = "js-utils"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ['cdylib']

[dependencies]
wasm-bindgen = "0.2.92"

开始写一些 rust 代码,在/src/lib.rs:

  • use wasm_bindgen::prelude::*; 导入所有wasm_bindgen的核心功能。以便后续直接调用
    #[wasm_bindgen]表明了下面的代码可以在 js 和 rust 中访问。

  • extern 将 js 函数导入 rust 中以供调用。代码中将alert函数声明后,在 rust 代码方法greet进行调用

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
pub fn greet(name: &str) {
    let str = format!("hello, World {name}");
    alert(&str);
}

为了方便测试,我们直接将这个发布为一个 npm 包

首先打包,--scope定义包命名空间

$> wasm-pack build --scope hboot

通过wasm-pack login登录你的 npm 账号,登录完成后,进入打包的目录下/pkg下进行发布

$> npm publish --access=public

发布完成后,就可以找一个前端项目测试安装发布的包。

$> npm i @hboot/js-utils

引入,调用greet函数,并传入自己的名称,启动服务,可以看到浏览器的弹窗。

在这我是采用本地软连接的方式导入到依赖中。

import * as jsUtils from "js-utils";

jsUtils.greet("hboot");

wasm-pack-lib-success.png

官方提供了一个前端项目模板wasm-app模板,可以通过 npm 创建项目。

$> npm init wasm-app rust-lib-web

官方提供了一个基于 webpack 的混合应用程序模板rust-webpack,可以编写 rust 代码,实时测试效果

$> npm init rust-webpack rust-webpack-web

这个混合应用是通过配置@wasm-tool/wasm-pack-plugin插件,在运行时编译 rust 代码,加载编译包呈现在页面。

具体的代码可以查看仓库rust-web-lib; 😀😀😀

初次编译时间都非常久,下一次编译时就会快很多。

配置wasm-bindgen-test 进行单元测试

每次发布的包都是需要测试的,测试完成后才能进行发布。使用wasm-bindgen-test用于测试 wasm-pack 编译为 wasm 的 rust 程序。

添加依赖

$> cargo add wasm-bindgen-test

编写测试,创建目录tests/web.rs来存储一些测试案例。

在无头浏览器中测试,增加代码,通过执行wasm-pack test --firefox发起测试

use wasm_bindgen_test::*;

wasm_bindgen_test_configure!(run_in_browser);

wasm-pack test --chrome执行测试提示chromedriver binaries are unavailable for this target估计是版本太新不匹配。

使用了 firefox 执行测试,运行成功,此时我们没有写任何的测试用例,会提示`no tests to run!。

和 rust 一样的测试书写方式,可以查看文章怎么写测试用例rust 自动化测试 ,区别就是把#[test]改为#[wasm_bindgen_test]

use js_utils;
use wasm_bindgen_test::*;

#[wasm_bindgen_test]
fn alert_name() {
    js_utils::greet("hboot");
    js_utils::hello();
}

wasm_bindgen_test_configure!(run_in_browser);

这里我们引入了当前项目作为依赖模块进行使用use js_utils,这里要注意的是 rust-wasm 和普通的 rust crate 不一样,我们需要指定crate-type = ["rlib"]才可以确保我们的库进行单元测试。

修改Cargo.toml:

[lib]
crate-type = ['cdylib', 'rlib']

好了,可以执行测试了wasm-pack test --firefox,看到如下输出:

wasm-pack-test-success.png

我们运行在浏览器中,测试启动一个服务将我们的代码运行在浏览器中,打开服务http://127.0.0.1:8000可以看到页面弹出的 alert

关闭弹窗,可以看到测试用例的运行情况:

wasm-pack-test-web-ok.png

默认情况下的wasm-pack test是在 node 环境下运行测试。

console_error_panic_hook将错误打印输出在浏览器中

在 rust 中代码出现逻辑错误,会panic!输出在控制台中,那我们调试前端 web 程序,肯定希望错误展现在浏览器中,方便查看。

安装依赖:

$> cargo add console_error_panic_hook

可以先看一下再没有使用console_error_panic_hook时,我们panic!信息,打包加载到我们测试项目中

without-console-error-panic.png

再配置使用console_error_panic_hook,有两种方式使用,我们使用第一种,可以去查看依赖文档了解。

use std::panic;

#[wasm_bindgen]
pub fn greet(name: &str) {
    panic::set_hook(Box::new(console_error_panic_hook::hook));

    // ...

    panic!("测试错误信息!");
}

在函数最顶部调用一次panic hook,再次打包测试。可以看到效果,这极大的方便了开发测试,定位问题。

with-console-error-panic.png

所有辅助的包都会有一个问题,就是会占用空间。所以在开发测试时需要加上,而正式发布包时则不需要。那就需要一个配置

[dependencies]
 - console_error_panic_hook = "0.1.7"
 + console_error_panic_hook = { version = "0.1.7", optional = true }

首先通过optional指定了这个依赖是可选的。然后通过命令参数--features指定启用哪些包,也可以配置包启用哪些特性。

当然,也可以使用Cargo.toml配置字段features来指定默认开启哪些依赖、特性。

[features]
default = []

此时,代码里就可以开启条件编译,通过#[cfg(feature = "console_error_panic_hook")]来标识

use std::panic;

#[wasm_bindgen]
pub fn greet(name: &str) {
    #[cfg(feature = "console_error_panic_hook")]
    panic::set_hook(Box::new(console_error_panic_hook::hook));

    // ...

    panic!("测试错误信息!");
}

再次打包则需要通过--features来指定开启哪些。

  • --all-features - 全部开启;
  • --no-default-features - 不开启,默认不设置时则不开启。

可以在此打包测试下wasm-pack build,可以看到控制台已经没有具体的错误信息所在的文件、行数、调用栈。我们再次加上参数wasm-pack build --features 'console_error_panic_hook'

打包测试,可以看到有具体的错误信息了。

wee_alloc 内存分配器

针对 wasm 内存分配,它可以生成更小的.wasm代码体积。适用于那些需要少量初始化动态大小的内存分配。它没有全局的默认分配性能好,但是代码占用空间小。

安装依赖:

$> cargo add wee_alloc

需要在全局引用,代替默认的内存的分配器:

extern crate wee_alloc;

// Use `wee_alloc` as the global allocator.
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

配置前的打包体积大概是54kb,配置后整个包的体积为45kb。我们测试包仅仅只有一个文件,看起来差别不大,还是可以看出一点。

它也可以作为可选依赖,配置后可手动指定开启。

在需要性能优先的代码库中,则不推荐使用。

认识WebAssembly

在上面重点介绍了 rust 如何编译成 wasm,并引入使用。这边也要了解 WebAssembly 真正的是什么。

wasm-pack-build-pkg.png

可以看到我们之前的测试库打包后生成了.wasm文件,我们直接点击打开是不行的,提示此文件是二进制文件或使用了不受支持的文本编码,所以无法在文本编辑器中显示。

通过 vscode 的插件WebAssembly 可以查看内部的wasm内容。在插件市场查找并安装

wasm-source.png

完全看不懂 🫠 先来一个简单的示例测试测试。可以使用后缀为.wat格式的文件书写wasm代码

(module
  (func (export "add") (param $p1 i32)(param $p2 i32) (result i32)
    local.get $p1
    local.get $p2
    i32.add))

上面的代码就是一个简单的add两数之和。

  • func定义函数,
  • (export "add") 定义这个函数被导出,可以在 js 中调用。
  • params定义参数, $p1定义参数的别名,在下面需要读取。
  • result定义返回结果。
  • local.get 读取参数。没有别名时,指定参数下标local.get 0标识读取第一个参数。
  • i32.add 以上参数之和并返回。

利用插件将.wat转换为.wasm存储,准备引入使用。

wat-to-wasm.png

index.html利用 fetch 加载 wasm 文件

fetch("./index.wasm")
  .then((res) => res.arrayBuffer())
  .then((bytes) => WebAssembly.instantiate(bytes))
  .then((res) => {
    console.log(res);
  });

查看最后的处理完的值,可看到res对象中存在instance对象包含了add方法

wasm-load-instance.png

这样我们就可以直接调用add方法了。

// ...
let num = res.instance.exports.add(35, 55);
alert(num);

可以看到执行效果了。这是一个简单的webAssembly示例,语法分析。

通过冒泡排序验证性能

要有强有力的验证说明,才能看到实际会提升多大的效果。rust 和 js 都使用自定义的冒泡排序算法,来执行上万条数据的排序, 看看执行时间做一个对比。

数据量少时,由于需要加载 wasm 所需的时间也会影响计算时间。在随着数据量变大,差距越来越大。

通过语言自带的排序算法可能由于内部各自实现的差异,执行效率受到其实现的影响。所以我们就自定义冒泡排序来测试

#[wasm_bindgen]
pub fn bublle_srot(arr: &mut [i32]) {
  let len = arr.len();
  for i in 0..len {
      for j in i+1..len {
          if arr[i] > arr[j] {
              arr.swap(i, j)
          }
      }
  }
}

在 js 中实现同样逻辑的算法,先用了一万条数据测试 js 比 wasm 快 4ms;数据增加大三万条时,js 明显就慢了,时间比 wasm 多了 100ms

bubble-sort-time.png

数据增大到五万条时,js 执行时间已经 wasm 慢了好几倍不止。

浏览器对数据排序应该是做了什么优化,第一次执行时间多出 wasm 好几倍;再次执行则多出几百毫秒。

通过 wasm 的加持,浏览器可以在更多领域施展拳脚。包括游戏、数据可视化、机器学习模型等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1608773.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

手写Java设计模式之抽象工厂模式,附源码解读

接上篇,抽象工厂模式将汽车的一些属性可以抽象出来,可以理解为给不同汽车品牌生成时加上不同的特性,如颜色等,具体代码如下: 引入颜色接口: public interface Colour {void fill(); }将颜色与汽车生成品牌…

Django项目使用uwsgi+nginx部署上线

Django项目使用uwsginginx部署上线 前言settings 配置安装uwsgi 和配置uwsgi推荐配置文件启用wsgi不使用nginx的配置(不推荐)使用nginx的配置 安装 nginx和配置niginx 配置 运行参考资料 前言 代码已经开发完成,正式部署上线 settings 配置…

公网IP地址如何申请SSL证书?有免费的IP ssl吗?

如果用户没有域名或只有公网IP地址或者不方便使用域名,IP地址ssl证书这一特殊的证书可以为IP地址实现HTTPS的安全保护,提高网站数据传输的安全性。 IP地址申请SSL证书的基本步骤 IP ssl证书下载---注册填写230916https://www.joyssl.com/certificate/sel…

Stable Diffusion UI 从安装到实现文字图片融合(光影字,错觉图)图片制作详细教程

前言 最近在实践大模型本地部署,前几天在本地部署了一个ChatGLM大模型,刚好环境搭好了,也支持跑Stable Diffusion,所以就安装了再尝试一下。 原因是之前在B站上有大佬做了一个Windows电脑能一键运行的Stable Diffusion的安装包&…

镜舟科技荣获金科创新社 2024 年度金融数据智能解决方案奖

近日, 镜舟科技凭借领先的金融实时数仓构建智能经营解决方案,在“金科创新社第六届金融数据智能优秀解决方案评选”活动中,成功入选“数据治理与数据平台创新优秀解决方案”榜单。 金科创新社主办的“鑫智奖”评选活动,旨在展示…

密码学 | 承诺:基本概念

目录 正文 1 承诺的交互 2 承诺的属性 3 硬币抛掷问题 3.1 朴素版方案 3.2 承诺版方案 🥑源自:https://en.wikipedia.org/wiki/Commitment_scheme 🥑写在前面:英文的承诺是 commitment scheme,否则很难进行…

Unity射击游戏开发教程:(1)玩家控制

玩家的移动 玩家控制和移动是视频游戏中最酷的事情之一,因为你正在控制游戏中的某些东西 现在游戏中的玩家是我们的蓝色方块英雄。我在游戏开发中了解到,游戏是用简单的对象制作原型,然后添加所有漂亮的艺术和声音。代码… 我们要做的第一件事是在游戏开始时为玩家提供一个…

uniapp H5项目 获取接口的二进制流转化成图片url(base64)

如果你使用的是uniapp, 并且你从接口获取下来的数据长这样: 想要把取到的数据展示成图片,那么你可以这样做: // 这是我们的项目封装的请求方法const res await this.$api.getKaptcha({originResponse: true, // 这样写是为了在request那边特…

js中let和var的区别

在JavaScript中,var、let和const都用于声明变量,但它们之间存在一些重要的区别。特别是let和var之间的区别,我们可以概括为以下几点: 作用域(Scope):var有函数作用域或全局作用域,而…

STM32实现硬件I2C通讯,读取MPU6050的ID号

今天学习了使用硬件I2C的方式成功读取MPU6050的ID号,特此记录一下过程: 首先需要学习的是MPU6050的初始化: 第一步:打开GPIOB的时钟(因为I2C2的引脚10,11在GPIOB上) 第二步:打开I2C2的时钟 …

09 MySQL--操作真题

1. 用一条 SQL 语句&#xff0c;查询出每门课程都大于 80 分的人。 分析&#xff1a; 去重查询出存在课程小于 80 分的人&#xff0c;设为集合A查询不在集合 A 中的人 # 第一步&#xff1a;找小于等于80分的学员姓名 select distinct name from t_student where fenshu <…

[Algorithm][滑动窗口][水果成篮][最大连续的一个数 Ⅲ][将x减到0的最小操作数]详细讲解

目录 1.水果成篮1.题目链接2.算法原理讲解3.代码讲解 2.找到字符串中所有字母异位词1.题目链接2.算法原理讲解3.代码实现 3.串联所有单词的字串1.题目链接2.算法原理讲解3.代码实现 3.最小覆盖字串1.题目链接2.算法原理讲解 1.水果成篮 1.题目链接 水果成篮 2.算法原理讲解 …

Docker in Docker的原理与实战

Docker in Docker&#xff08;简称DinD&#xff09;是一种在Docker容器内部运行另一个Docker实例的技术。这种技术允许用户在一个隔离的Docker容器中创建、管理和运行其他Docker容器&#xff0c;从而提供了更灵活和可控的部署选项。以下是DinD的主要特点&#xff1a; 隔离性&am…

【工厂模式】工厂方法模式、抽象工厂模式-简单例子

简单工厂模式&#xff0c;请跳转到我的另一篇博客【工厂模式】简单工厂模式-简单例子-CSDN博客 四、工厂方法模式 &#xff08;1&#xff09;这部分还是不变&#xff0c;创建一个Car接口&#xff0c;和两个实现类。 public interface Car {void name(); }public class WuLing…

文献补充材料怎么查找获取

最近很多同学求助外文文献补充材料&#xff0c;有的同学在学校提供的资源中能找到全文却无法下载文献的补充材料&#xff0c;这是因为该校只收录了全文而没有收录补充材料。那么文献补充材料怎么查找又如何获取呢&#xff1f;请看本文的经验分享&#xff1a; 1、文献的补充材料…

在Linux系统中搜索当前路径及其子目录下所有PDF文件中是否包含特定字符串

目录标题 方法一&#xff1a;pdfgrep方法二&#xff1a;使用find和xargs与pdftotext&#xff08;将PDF转换为文本&#xff09;组合&#xff0c;然后用grep搜索 方法一&#xff1a;pdfgrep pdfgrep -ri "rockchip" .方法二&#xff1a;使用find和xargs与pdftotext&am…

Yolov8项目实践——基于yolov8与OpenCV实现目标物体运动热力图

概述 在数据驱动和定位的世界中&#xff0c;对数据进行解释、可视化和决策的能力变得日益重要。这表明&#xff0c;使用正确的工具和技术可能是项目成功的关键。在计算机视觉领域&#xff0c;存在许多技术来解释从视频&#xff08;包括录像、流媒体或实时视频&#xff09;中获…

【HC32L110】华大低功耗单片机启动文件详解

本文主要记录华大低功耗单片机 HC32L110 的 汇编启动过程&#xff0c;包括startup_hc32l110启动文件详细注释 目录 1.启动文件的作用2.堆栈定义2.1 栈2.2堆 3.向量表4.复位程序5.中断服务程序6.堆栈初始化启动过程详解7.1从0地址开始7.2在Reset_Handler中干了啥&#xff1f; 8.…

ContextMenuStrip内容菜单源对象赋值学习笔记(含源码)

一、前言 MetroTileItem属于第三方控件,无法定义ContextMenuStrip属性 想实现某子项点击菜单时,与源控件(按钮metroTileItem)的某值对应,用于动态控制按钮的状态或方法 1.1 效果 二、实现方法 2.1 方法1 (代码,说明见注释) private void metroTileItem_MouseDown(o…

python_django中小学家校互动系统vue_flask家校联系

实现了一个完整的家校互动系统&#xff0c;其中主要有作业信息模块、学校管理员模块、学生学籍模块、学生成绩模块、学科模块、系统新闻模块、系统公告模块、校内新闻模块、校内公告模块、用户表模块、token表模块、关于我们模块、收藏表模块、年级模块、家长模块、教师模块、互…