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

推荐订阅源

V
Visual Studio Blog
Martin Fowler
Martin Fowler
aimingoo的专栏
aimingoo的专栏
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
C
Cybersecurity and Infrastructure Security Agency CISA
C
Cisco Blogs
S
Securelist
博客园 - Franky
P
Proofpoint News Feed
量子位
雷峰网
雷峰网
Security Latest
Security Latest
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Latest news
Latest news
L
Lohrmann on Cybersecurity
W
WeLiveSecurity
月光博客
月光博客
Hacker News: Ask HN
Hacker News: Ask HN
宝玉的分享
宝玉的分享
GbyAI
GbyAI
小众软件
小众软件
M
MIT News - Artificial intelligence
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
Secure Thoughts
The Cloudflare Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
IT之家
IT之家
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Threat Research - Cisco Blogs
Stack Overflow Blog
Stack Overflow Blog
有赞技术团队
有赞技术团队
Attack and Defense Labs
Attack and Defense Labs
Y
Y Combinator Blog
Scott Helme
Scott Helme
O
OpenAI News
Know Your Adversary
Know Your Adversary
AWS News Blog
AWS News Blog
阮一峰的网络日志
阮一峰的网络日志
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
V
Vulnerabilities – Threatpost
博客园 - 【当耐特】
K
Kaspersky official blog
Microsoft Azure Blog
Microsoft Azure Blog
S
SegmentFault 最新的问题
Forbes - Security
Forbes - Security
腾讯CDC
NISL@THU
NISL@THU
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Tailwind CSS Blog

卢贤泼的博客

漫长的告别 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