10 个稳定版本
1.2.1 | 2024 年 1 月 18 日 |
---|---|
1.2.0 | 2024 年 1 月 17 日 |
1.1.3 | 2023 年 12 月 16 日 |
1.0.3 | 2021 年 4 月 27 日 |
1.0.2 | 2021 年 3 月 31 日 |
#16 在 WebAssembly 中
4,900 每月下载量
用于 5 crates
23KB
166 代码行
wasm-bindgen-rayon
是一个适配器,通过 WebAssembly (via wasm-bindgen, Web Workers 和 SharedArrayBuffer 支持) 在 Web 上启用基于 Rayon 的并发。
使用方法
在 Rust 中,WebAssembly 线程支持还不是一等公民,因此在使用此 crate 时有一些注意事项。请耐心等待 :)
要查看快速演示,请访问 https://rreverser.com/wasm-bindgen-rayon-demo/。
注意事项
在我们开始之前,请查看 wasm-bindgen 线程文档 中列出的注意事项。虽然这个库专门针对 Rayon 并为你自动提供必要的垫片,但其中一些注意事项仍然适用。
设置
为了在 Web 上使用 SharedArrayBuffer
,您需要启用 跨源隔离策略。请查看相关文章以获取详细信息。
然后,将 wasm-bindgen
、rayon
和此 crate 作为依赖项添加到您的 Cargo.toml
[dependencies]
wasm-bindgen = "0.2.74"
rayon = "1.8"
wasm-bindgen-rayon = "1.0"
然后,重新导出 init_thread_pool
函数
pub use wasm_bindgen_rayon::init_thread_pool;
// ...
这将在您库的最终生成的 JavaScript 中暴露一个异步的 initThreadPool
函数。
您需要在主线程上实例化模块后立即调用它,以便在调用实际库函数之前准备线程池
import init, { initThreadPool /* ... */ } from './pkg/index.js';
// Regular wasm-bindgen initialization.
await init();
// Thread pool initialization with the given number of threads
// (pass `navigator.hardwareConcurrency` if you want to use all cores).
await initThreadPool(navigator.hardwareConcurrency);
// ...now you can invoke any exported functions as you normally would
使用 Rayon
像平常一样使用 Rayon 迭代器,例如
#[wasm_bindgen]
pub fn sum(numbers: &[i32]) -> i32 {
numbers.par_iter().sum()
}
将从 JavaScript 侧接受一个 Int32Array
并使用所有可用的线程计算其值的总和。
构建 Rust 代码
需要注意的第一个限制是,您必须使用 wasm-bindgen
/wasm-pack
的 web
目标(--target web
)。
为什么?
这是因为 Wasm 代码需要将其自己的对象(WebAssembly.Module
)在创建新线程时与它们共享。这个对象仅可以从 --target web
和 --target no-modules
输出中访问,但因为我们还使用了 JS 片段功能,所以我们进一步将其限制为仅 --target web
。
另一个问题是,WebAssembly 目标的 Rust 标准库是在不支持线程的情况下构建的,以确保最大程度的可移植性。
由于我们希望标准 API(如 Mutex
、Arc
等)能够正常工作,因此您需要使用 nightly 编译器工具链,并传递一些标志来重新构建标准库,这包括您自己的代码。
为了减少破坏的风险,强烈建议使用固定的 nightly 版本。例如,撰写本文时最新的稳定 Rust 版本是 1.66,对应于 nightly-2022-12-12
,它经过测试并且与这个包兼容。
使用配置文件
配置这些标志的最简单方法是
-
在您的项目目录中创建一个名为
rust-toolchain
的文件,并放入字符串nightly-2022-12-12
。这将告诉 Rustup 默认为您的项目使用 nightly 工具链。 -
在您的项目目录中的
.cargo/config.toml
文件中放入以下内容[target.wasm32-unknown-unknown] rustflags = ["-C", "target-feature=+atomics,+bulk-memory,+mutable-globals"] [unstable] build-std = ["panic_abort", "std"]
这告诉 Cargo 重新构建支持 Wasm 原子操作的标准库。
然后,像往常一样使用 --target web
运行 wasm-pack
wasm-pack build --target web [...normal wasm-pack params...]
使用命令行参数
如果您不想默认配置这些参数,您可以将它们作为构建命令的一部分传递。
在这种情况下,整个命令如下所示
RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals' \
rustup run nightly-2022-12-12 \
wasm-pack build --target web [...] \
-- -Z build-std=panic_abort,std
它看起来有点吓人,但它处理了所有事情 - 选择 nightly 工具链、启用所需的特性以及告诉 Cargo 重新构建标准库。您只需复制一次,并希望永远忘记它 :)
功能检测
并非所有浏览器 都支持 WebAssembly 线程,因此您可能需要构建两个版本 - 一个支持线程,另一个不支持 - 并在 JavaScript 侧使用功能检测来选择正确的版本。
您可以使用 wasm-feature-detect
库来实现此目的。代码大致如下
import { threads } from 'wasm-feature-detect';
let wasmPkg;
if (await threads()) {
wasmPkg = await import('./pkg-with-threads/index.js');
await wasmPkg.default();
await wasmPkg.initThreadPool(navigator.hardwareConcurrency);
} else {
wasmPkg = await import('./pkg-without-threads/index.js');
await wasmPkg.default();
}
wasmPkg.nowCallAnyExportedFuncs();
与各种打包工具一起使用
WebAssembly 线程在底层使用 Web Workers 实例化具有相同 WebAssembly 模块 & 内存的其他线程。
wasm-bindgen-rayon 内部提供了所需的 JS 代码,并使用 在各种打包器中被识别的语法。
与 Webpack 一起使用
如果您正在使用 Webpack v5(版本 >= 5.25.1),您不需要做任何特殊操作,因为它已经默认支持 打包 Workers。
与 Parcel 一起使用
Parcel v2 也识别所使用的语法,并默认工作。
与 Rollup 一起使用
对于 Rollup,您需要 @surma/rollup-plugin-off-main-thread
插件(版本 >= 2.1.0),它提供了相同的功能,并已与这个包进行过测试。
或者,您可以使用内置必要插件的 Vite。
不使用打包工具的使用
默认的JS胶水设计得非常好,可以与打包器和代码拆分配合使用,但遗憾的是,由于对导入路径的处理不同,在浏览器中表现不佳(参见 WICG/import-maps#244
)。
如果您想在没有打包器的情况下构建此库,请在您的 Cargo.toml
中启用 wasm-bindgen-rayon
的 no-bundler
功能。
wasm-bindgen-rayon = { version = "1.0", features = ["no-bundler"] }
许可证
此软件包采用 Apache-2.0 许可。
依赖项
~2.4–4.5MB
~81K SLoC