惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

SecWiki News
SecWiki News
I
InfoQ
The Cloudflare Blog
人人都是产品经理
人人都是产品经理
博客园 - Franky
T
Tailwind CSS Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
量子位
博客园_首页
罗磊的独立博客
V
V2EX
李成银的技术随笔
大猫的无限游戏
大猫的无限游戏
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
True Tiger Recordings
Vercel News
Vercel News
Cyberwarzone
Cyberwarzone
Cisco Talos Blog
Cisco Talos Blog
F
Fox-IT International blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
M
Microsoft Research Blog - Microsoft Research
Know Your Adversary
Know Your Adversary
爱范儿
爱范儿
The Register - Security
The Register - Security
G
Google Developers Blog
The Hacker News
The Hacker News
Malwarebytes
Malwarebytes
S
Securelist
博客园 - 三生石上(FineUI控件)
Jina AI
Jina AI
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
S
SegmentFault 最新的问题
博客园 - 叶小钗
F
Fortinet All Blogs
Apple Machine Learning Research
Apple Machine Learning Research
宝玉的分享
宝玉的分享
博客园 - 聂微东
T
Threatpost
博客园 - 【当耐特】
D
Docker
P
Privacy & Cybersecurity Law Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
G
GRAHAM CLULEY
V
Visual Studio Blog
C
Cisco Blogs
IT之家
IT之家
S
Security Archives - TechRepublic
Latest news
Latest news
阮一峰的网络日志
阮一峰的网络日志

卢贤泼的博客

漫长的告别 04: 答案在别处 | 卢贤泼的博客 漫长的告别 03: 人生修炼 | 卢贤泼的博客 漫长的告别 02: 赢过昨天,输给明天 | 卢贤泼的博客 漫长的告别 01: 不期而遇 | 卢贤泼的博客 漫长的告别 00: 创刊号 | 卢贤泼的博客 2020 这一年 | 卢贤泼的博客 热血合唱团 | 卢贤泼的博客 我的 macOS 常用工具和配置 | 卢贤泼的博客 《The Social Dilemma》智能陷阱 | 卢贤泼的博客 《目光》:向光而行 | 卢贤泼的博客 1024 的周末 | 卢贤泼的博客 读书笔记:小狗钱钱 | 卢贤泼的博客 纪录片《人生果实》 | 卢贤泼的博客 国庆假期的一些小事 | 卢贤泼的博客 这个博客背后的技术细节 | 卢贤泼的博客 多重身份 | 卢贤泼的博客 一往无前 | 卢贤泼的博客 Python for JavaScript Developer | 卢贤泼的博客 2019 这一年 | 卢贤泼的博客 VueConf 2019 | 卢贤泼的博客 2018 这一年 | 卢贤泼的博客 伙伴,路途上的星光 | 卢贤泼的博客 《黑客与画家》--- 一个程序员的自我修养 | 卢贤泼的博客
WebAssembly With Rust | 卢贤泼的博客
alanlupublic@gmail.com (卢贤泼) · 2021-01-10 · via 卢贤泼的博客

4 大主流浏览器已经都支持了 WebAssembly,同时也很多项目中得到了实际应用,本文将分享如何构建一个 WebAssembly (简称 WASM) 以及在浏览器中的使用。

使用 Rust 构建一个 WASM

try-rust_ScreenShot2021-01-10at12.54.29PM

能够构建 WASM 的语言有很多,本文将使用 Rust 来构建,分享如何从 0 构建一个 WASM。

基础环境初始化

  1. 安装 Rust 环境
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  1. 初始化一个 WASM 项目
cargo new --lib hello-wasm

更多详细的步骤可以看:Rust 和 WebAssembly 用例

如何将 Rust 构建为 WASM

Rust 社区中提供了封装好的工具wasm-pack,可以直接将 rust 的代码打包为一个 npm 包

在 Rust 中调用 JS 中的方法可以通过 wasm-bindgen

在做完了以上的一些方法之后,我们可以运行下面的命令,借助 wasm-pack 将 Rust 打包为一个 WebAssembly

wasm-pack build -t web

打包之后我们会得到得到一个新的 pkg 目录,里面有对应的构建产物,我们可以直接在 Web 中使用,下面是一段代码示例

<script type="module">
  import init, { greet } from './pkg/hello_wasm.js'
  init().then(() => {
    greet('WASM')
  })
</script>

WASM 是如何和 Web 进行交互的

经过上面的步骤,整体跑起来了 Hello World,上面相应的代码在这个仓库中 luxp/code-parctice/hello-wasm

下面我们从源码的角度,看看 WASM 是如何工作的

加载 WASM

wasm-pack 已经直接帮我们打包好了对应的模块,可以直接在浏览器中是用原生 import

<script type="module">
  import init, { greet } from './pkg/hello_wasm.js'
  init().then(() => {
    greet('WASM')
  })
</script>

pkg/hello_wasm.js

实际加载 WASM 的是在 pkg/hello_wasm.js 这个文件中,整体的流程可以简化以下的过程。 WebAssembly.instantiate 文档

fetch('hello_wasm_bg.wasm').then(async (module) => {
  let imports = {}
  let wasm = await WebAssembly.instantiate(module, imports)

  // wasm 这个就是 WebAssembly 的实例了
  return wasm
})

调用 WASM 中的方法

我们并不能直接调用 wasm 中的方法,而是要经过一层中转,可以看到在下面的代码中,我们调用的 greet 方法实际是从 hello_wasm.js 这个文件中导出的

<script type="module">
  import init, { greet } from './pkg/hello_wasm.js'
  init().then(() => {
    greet('WASM')
  })
</script>

实际的 greet 的代码

export function greet(name) {
  var ptr0 = passStringToWasm0(
    name,
    wasm.__wbindgen_malloc,
    wasm.__wbindgen_realloc
  )
  var len0 = WASM_VECTOR_LEN
  wasm.greet(ptr0, len0)
}

在调用 greet 时,会先将传入的 name 值写入 WASM 的内存中,最终传入给 WASM 的是内存的地址,而不是字符串

WASM 中如何调用 Web 中的方法

Rust 中的代码,借助wasm_bindgen通过 extern "C" 定义了要从外部引入的函数

#[wasm_bindgen]
extern "C" {
    // 在最终编译的结果中,alert 这一块会被替换
    pub fn alert(s: &str);
}

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

在 WASM 初始化注入,流程可简化为下面

fetch('hello_wasm_bg.wasm').then(async (module) => {
  let imports = {}
  imports.alert = function (arg0, arg1) {
    // 获取到内存区域
    alert(getStringFromWasm0(arg0, arg1))
  }
  // 通过 imports 注入
  let wasm = await WebAssembly.instantiate(module, imports)

  // wasm 这个就是 WebAssembly 的实例了
  return wasm
})

以上通过共享同一块内存和注入函数的方式,实现了 JS 和 WASM 的双向通信

WASM 通信的性能消耗

修改了以下 Rust 中的代码,让其简单做一个字符串的拼接


#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    let mut result: String = "prefix_".to_owned();
    result.push_str(name);

    return result;
}

同时在 HTML 中使用 JS 也实现一个版本

<script type="module">
  import init, { greet } from './pkg/hello_wasm.js'
  init().then((wasm) => {
    console.time('greetWASM')
    for (let i = 0; i < 20000; ++i) {
      greet('Test')
    }
    console.timeEnd('greetWASM')

    function greetJs(str) {
      return 'prefix_' + str
    }
    console.time('greetJS')
    for (let i = 0; i < 20000; ++i) {
      greetJs('Test')
    }
    console.timeEnd('greetJS')
  })
</script>

在 Chrome 87 版本下,运行的结果如下图

try-rust_ScreenShot2021-01-10at3.05.30PM

整体上来看相比于纯 JS 的执行,WASM 在数据传输上还是会消耗一定的时间,但整体的耗时相比于 Rust 在高计算复杂度场景下带来的提升影响不大,2 万次的调用耗时在 40ms 内

#tech