0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

什么是wasm组件?使用Rust开发wasm组件实战

OSC开源社区 来源:凹凸实验室 2023-09-22 11:30 次阅读

什么是wasm组件?

wasm 全称 WebAssembly,是通过虚拟机的方式,可以在服务端、客户端如浏览器等环境执行的二进制程序。它有速度快、效率高、可移植的特点。

对我们 Web 前端工程最大的好处就是可以在浏览器端使用二进制程序处理一些计算量大的处理,使用他比 javascript 快的特点优化性能。

目前浏览器对wasm的兼容性如下:

cd675886-5871-11ee-939d-92fbcf53809c.jpghttps://img10.360buyimg.com/imagetools/jfs/t1/180904/35/36038/170761/64ded9bdF6f54c383/e85e037cdd4fa1fd.jpg

在移动端除了 android 4.4 和 ios 10 下不支持外,其他版本都能提供支持。还需要注意的是 wasm 有可能占用大量内存,使用第三方包含 wasm 调用的组件需要注意内存占用防止闪退。

为什么用Rust?

wasm模块 可以用多种语言来编译,包括 C/C++/C#、Rust、JAVA、Go。在这里使用 Rust 是因为他有严格的内存管理机制,从语法上尽量避免内存溢出,让工程师写出更安全的程序。

而且还有配套的工具 wasm-pack,让使用 Rust 编写的代码,编译包装成 npm 包,让使用这段程序的其他代码可以像使用其他公共库一样调用,不需要额外学习成本。

工具安装

安装 rustup,它是 Rust 安装器和版本管理工具。对于 web 前端来说相当于 nvm 这样的工具。按照 Rust 官网的方法安装:https://www.rust-lang.org/zh-CN/tools/install同时也会安装 cargo,它是 Rust 的构建工具和包管理器。对于 web 前端来说相当于 npm 这样的工具。

安装 wasm-pack,他是上文提到的把 Rust 程序编译包装成 wasm 组件的工具。同样按照 wasm-pack 官网的方法安装:https://rustwasm.github.io/wasm-pack/installer/

使用 wasm 模板使用 wasm-pack 提供的模板可以快速生成 Rust 的 wasm 项目。

cargogenerate--githttps://github.com/rustwasm/wasm-pack-template

输入希望的项目目录名称,将新建目录并在其中生成项目。

在目录下我们可以看到几个文件,其中一个是 Cargo.toml ,这个是 Rust 项目的描述文件,对于 web 前端来说相当于 package.json 文件。

项目目录下还有一个 src 目录,里面有 lib.rs 和 utils.rs 两个文件,其中 lib.rs 这个文件就是我们主要的逻辑入口,他引用了 wasm-bindgen 库来输出暴露给外部调用的接口,在函数之前加上#[wasm_bindgen]可以让外部调用这个方法。

编译项目

本来 Rust 的项目编译用的是 cargo build 的命令,但是我们这里是希望编译 wasm 组件,所以用的是 wasm-pack build 命令。

执行后会在项目目录下的 pkg 目录下生成编译后的产品,是一个 npm 包的结构。需要调用这个组件的逻辑只需要像其他公共包一样 import 就可以使用了。

实战

以上的就是 wasm-pack 官方的教程,还有其他组件测试、发布等的流程先不在这里介绍了。以下用一个实际开发中的模块来说一下开发 wasm 组件过程中遇到的问题和解决方法。

背景—

需要使用的 wasm 组件是一个优化3D模型的方法,传入一个模型的顶点信息和距离阈值,比较每个顶点位置之间的距离,如果没达到阈值距离就合并这两个顶点,以达到减少顶点的优化目的。

原逻辑是使用 javascript 编写的,在模型顶点数量比较多的时候执行的时间比较长。这种大量计算的情况就很适合使用 wasm 来处理。

数据传递—

顶点信息是存储在一个 Float32Array 的数组中的,而 wasm 设计上除了 int 和 float 类型(对应 javascript 就是 number 类型)可以直接传递外,其他的类型都通过地址来传递。这对我们的程序来说是好消息,因为顶点信息的数据非常多,如果以值传递,就需要做数据复制,这个过程消耗的时间可能比我们换成 wasm 处理减少的时间还要多。得益这个特点,我们的入参可以直接传入。

/*---rust----*/

//rust获取javasctipt数据
pubfnadd_attribute(&mutself,attribute:&Float32Array,item_size:u32){
self.attributes.push(BufferAttribute{
array:attribute.to_vec(),
item_size,
});
}
/*---javascript----*/

//javascript传递数据到rust
for(constnameofattributeNames){
constattr=attrArrays[name]
bg.add_attribute(attr.array,attr.itemSize)
}

而计算后的结果,wasm 也提供了返回数组的指针和数组长度的方法,javascript 可以读取 wasm 的内存空间,根据这两个值构造新的顶点信息Float32Array。

/*---rust----*/

//返回指定数据的内存指针位置
pubfnget_attribute_ptr(&self,index:usize)->*constf32{
self.attributes[index].array.as_ptr()
}

//返回指定数据的长度
pubfnget_attribute_length(&self,index:usize)->usize{
self.attributes[index].array.len()
}
/*---javascript----*/

//javascript或取rust内存空间中的指定部分,构建Float32Array
constptr=bg.get_attribute_ptr(i)
constlength=bg.get_attribute_length(i)

constbuffer=newattr.array.constructor(wasm.getMemory().buffer,ptr,length)

数据类型—

合并顶点计算的逻辑中,有一段是这样的:每个顶点的位置、UV等信息,经过给定的精度计算后,生成一个特征值,之后比较每个顶点的特征值,如果是相同的话就表示这两个顶点可以合并。

原 javascript 版本的代码是逐个信息按顺序,加上分隔号,拼成一个字符串。

Rust 版本的代码如果也按同样的方法处理,因为顶点的信息量是不定的,有可能只有位置信息,也有可能有UV、法线、颜色等信息,所以生成的特征值字符串长度也不确定。

Rust 对於可变长度的字符串使用 String 类型,每次对字符串使用push_str方法增加内容。得到的结果 wasm 版本的执行速度跟 javascript 版本相差不大,甚至在某些情况下耗时还更多,经过逐个过程作排查,发现是在生成特征值和在表中查询特征值这个过程中花费的时间比较多。

根据程序的意图,特征值并不一定要是字符串,只需要在不同输入值的时候能够输出相关的值就可以,这跟生成 hash 值的需求是一样的,于是考虑将特征值生成替换成 hash 值计算。

因为在存储特征值的表使用了std::hash_map类型,于是 hash 值也使用了其下的std::DefaultHasher类来计算

usestd::DefaultHasher;

...

letmuthasher=DefaultHasher::new();

forjin0..self.attributes.len(){
...

letvalue=(attr.array[i*attr.item_sizeasusize+indexasusize]
*self.shift_multiplier)
.trunc()asi32;

hasher.write_i32(value);

...
}

lethash=hasher.finish();

需要注意的是对写入不同类型的内容,需要调用不同的方法,顶点信息中的值是正负值都用,经过精度计算后取整得到的值类型是i32,所以用write_i32来写入内容。

生成的 hash 值为u64,作为hash_map的key记录对应顶点的序号。

替换特征值的类型之后,wasm 版本的耗时达到了 javascript 版本的 1/2,基本符合 wasm 设计的性能范围。

适配打包工具—

wasm-pack 工具打包出来的 npm 包,可以直接在webpack下加载并调用运行。

我们原本的项目使用 vite 构建,vite 对import wasm 组件策略和 webpack 的不一样,vite 加载会返回一个加载方法,调用加载方法会返回一个 Promise,resolve 后才会返回跟 webpack 加载一样的 wasm 组件。

我们要对 wasm-pack 生成的产物作一些修改,假设我们的 wasm 组件命名为 merge_vertice_wasm,生成的主 js 文件应该会命名为merge_vertice_wasm.js,内容如下:

import*aswasmfrom'./merge_vertice_wasm_bg.wasm'
import{__wbg_set_wasm}aswasm_bgfrom'./merge_vertice_wasm_bg.js'
__wbg_set_wasm(wasm);
export*from'./merge_vertice_wasm_bg.js'

为兼容 vite 的加载策略,修改成下面的内容

import*aswasmfrom'./merge_vertice_wasm_bg.wasm'
import*aswasm_bgfrom'./merge_vertice_wasm_bg.js'

letmemory
if(wasm.default){
wasm.default({
'./merge_vertice_wasm_bg.js':wasm_bg,
}).then(_wasm=>{
memory=_wasm.memory
wasm_bg.__wbg_set_wasm(_wasm)
})
}else{
memory=_wasm.memory
wasm_bg.__wbg_set_wasm(wasm)
}
export*from'./merge_vertice_wasm_bg.js'

exportfunctiongetMemory(){
returnmemory
}

就可以在 webpack 和 vite 下都可以顺利加载并运行了。

其中增加了getMemory的方法供外部获取 wasm 组件的内存空间。

wasm 调用 javascript 方法—

当我们在调试和测试性能表现时,需要打印日志,由于我们的 wasm 跑在浏览器环境中,我们需要调用 javascript 的方法,比如console.log和console.time。

wasm-bindgen 库提供了 web-sys 的组件,让 Rust 可以调用这些方法。

首先需要在cargo.toml中添加 web-sys 的依赖,并声明需要用到的特性:

[dependencies]
wasm-bindgen="0.2.84"

[dependencies.web-sys]
version="0.3.64"
features=["console"]

这样在下次编译的时候,cargo 就会自动处理这些依赖,将会下载并构建。

然后在我们的 Rust 文件中,加入对 web-sys 的引用:

externcrateweb_sys;

就可以调用 javascript 的 console 下的方法了:

//调用console.log
web_sys::log_1(&JsValue::from(logContent));

//调用console.time(label)
web_sys::time_with_label(label);

//调用console.timeEnd(label)
web_sys::time_end_with_label(label);

原 javascript 版本优化模型耗时:

cd788f3e-5871-11ee-939d-92fbcf53809c.jpg

https://img14.360buyimg.com/imagetools/jfs/t1/109410/21/37527/8537/64dedd1cFe4c8c5c4/596fc2d36cc9fe5c.jpg

wasm 版本优化模型耗时:

cd87b464-5871-11ee-939d-92fbcf53809c.jpg

https://img12.360buyimg.com/imagetools/jfs/t1/188745/32/36809/10529/64dedd1cF49a8b5cc/8dea820d278ad577.jpg

总结

以上为根据官网文档把模型合并顶点优化方法迁移为 wasm 版本的开发经历,从安装工具到发布、调试的整个过程。

中间因为对 Rust 数据类型的不熟悉和对不同前端构建工具对 wasm 组件处理的不同不够清晰,在开发过程中遇到的问题和解决方法。

Rust 版本的代码逻辑基本上是从 javascript 版本翻译过来的,其中应该还有在 Rust 环境下的优化手段,将在之后的学习中继续迭代。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Web
    Web
    +关注

    关注

    2

    文章

    1238

    浏览量

    68467
  • C++
    C++
    +关注

    关注

    21

    文章

    2066

    浏览量

    72900
  • Rust
    +关注

    关注

    1

    文章

    223

    浏览量

    6387

原文标题:给Web前端工程师看的用Rust开发wasm组件实战

文章出处:【微信号:OSC开源社区,微信公众号:OSC开源社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    从WasmEdge运行环境读写Rust Wasm应用的时序数据

    WebAssembly (Wasm) 正在成为一个广受欢迎的编译目标,帮助开发者构建可迁移平台的应用。最近 Greptime 和 WasmEdge 协作,支持了在 WasmEdge 平台上的 Wasm 应用通过 MySQL 协议
    的头像 发表于 12-22 11:03 474次阅读

    鸿蒙开发实战-(ArkUI)List组件和Grid组件的使用

    一系列相同宽度的列表项,连续、多行呈现同类数据,例如图片和文本。常见的列表有线性列表(List列表)和网格布局(Grid列表): 为了帮助开发者构建包含列表的应用,ArkUI提供了List组件和Grid
    发表于 01-18 20:18

    【RISC-V 生态软件系列】Waft基础教程三:模拟器的使用

    隆重宣布,waft应用现在可以脱离设备,运行在pc端上的模拟器了。waft应用开发又提效了,开发者可以在pc模拟器上调试好应用开发,页面调试,然后在真机上运行。下面介绍模拟器的使用。准备工作确保
    发表于 03-08 09:36

    Wasm软件生态系统安全分析

    ,致⼒于宣传、实施、推⼴Wasm与OpenHarmony的集成,在OpenHarmony终端设备上可以安全高效的运行第三方开发者用 C、C++、Rust 等语言编写的 Wasm 程序,
    发表于 09-05 15:29

    微信小程序如何开发?微信小程序教程视频常用组件API开发项目实战

    本文档的主要内容介绍的是微信小程序如何开发?微信小程序教程视频微信小程序入门与实战 常用组件API开发技巧项目实战详细的介绍了
    发表于 08-28 14:41 88次下载

    迅雷链正式推出了WASM虚拟机模块的源代码和代码库

    随着区块链应用对虚拟机以及智能合约的需求增多,区块链虚拟机技术也在逐渐完善。目前,基于WASM的虚拟机在速度和性能方面都有了显著提升,且支持C、C++等多种编程语言,开发门槛更低。为了让区块链应用开发更便捷,迅雷链推出了双虚拟机
    发表于 08-15 11:42 4258次阅读
    迅雷链正式推出了<b class='flag-5'>WASM</b>虚拟机模块的源代码和代码库

    如何使用Rust进行Wasm合约开发

    Ontology Wasm 自从上线测试网以来,得到了社区开发人员的极大关注。因为这项技术使得业务逻辑复杂的 dApp 合约上链成本降低,极大丰富 dApp 生态。
    发表于 08-22 11:09 5145次阅读

    如何使用C++进行Ontology Wasm合约开发

    Ontology Wasm 自从上线测试网以来便受到了社区开发人员的极大关注。Ontology Wasm 的上线将使得业务逻辑复杂的 dApp 合约上链成本降低,极大丰富 dApp 生态。在进行 Ontology
    发表于 08-29 17:30 817次阅读

    重大性能更新:Wasm 后端将利用 SIMD指令和 XNNPACK多线程

    3 月,我们为 TensorFlow.js 推出了一个新的 WebAssembly(Wasm) 加速后端(继续阅读以进一步了解 Wasm 及其重要性)。今天,我们很高兴宣布一项重大性能更新
    的头像 发表于 09-30 15:15 8606次阅读
    重大性能更新:<b class='flag-5'>Wasm</b> 后端将利用 SIMD指令和 XNNPACK多线程

    vim.wasm Vim-WebAssembly编译器

    ./oschina_soft/vim.wasm.zip
    发表于 06-22 09:57 6次下载
    vim.<b class='flag-5'>wasm</b> Vim-WebAssembly编译器

    浅析Wasm-bpf架起Webassembly和eBPF内核可编程的桥梁

    Wasm 最初是以浏览器安全沙盒为目的开发的,发展到目前为止,WebAssembly 已经成为一个用于云原生软件组件的高性能、跨平台和多语言软件沙箱环境
    的头像 发表于 02-13 11:40 493次阅读

    在WebAssembly中使用Rust编写eBPF程序并发布OCI镜像

    WebAssembly(Wasm)最初是以浏览器安全沙盒为目的开发的,发展到目前为止,WebAssembly 已经成为一个用于云原生软件组件的高性能、跨平台和多语言软件沙箱环境,Wasm
    的头像 发表于 02-14 18:10 907次阅读

    鸿蒙开发实战:【性能调优组件

    性能调优组件包含系统和应用调优框架,旨在为开发者提供一套性能调优平台,可以用来分析内存、性能等问题
    的头像 发表于 03-13 15:12 126次阅读
    鸿蒙<b class='flag-5'>开发</b><b class='flag-5'>实战</b>:【性能调优<b class='flag-5'>组件</b>】

    鸿蒙实战开发学习【FaultLoggerd组件

    Faultloggerd部件是OpenHarmony中C/C++运行时崩溃临时日志的生成及管理模块。面向基于 Rust 开发的部件,Faultloggerd 提供了Rust Panic故障日志生成能力。系统
    的头像 发表于 03-17 20:39 132次阅读
    鸿蒙<b class='flag-5'>实战</b><b class='flag-5'>开发</b>学习【FaultLoggerd<b class='flag-5'>组件</b>】

    OpenHarmony实战开发-如何实现组件动画。

    ArkUI为组件提供了通用的属性动画和转场动画能力的同时,还为一些组件提供了默认的动画效果。例如,List的滑动动效,Button的点击动效,是组件自带的默认动画效果。在组件默认动画效
    的头像 发表于 04-28 15:49 78次阅读
    OpenHarmony<b class='flag-5'>实战</b><b class='flag-5'>开发</b>-如何实现<b class='flag-5'>组件</b>动画。