🌊

Web Worker+WASM+Parcel+TypeScriptでマルチスレッド実行: Rustが征く(12)

2022/02/21に公開

Viteで行きたかったがParcelで我慢

関連記事:

Rustが征くシリーズ過去記事

さらに要素多過ぎ問題

------------------- ↓ 前書はここから ↓-------------------

以前の記事
Web Worker+WebAssemblyのパフォーマンス計測: Rustが征く(11)にて、
WebAssemblyマルチスレッドをESモジュールで実行するのが確認できた。
となれば、バンドラーと組み合わせることができるかもしれない。

ということで、今回はバンドラーParcelを使用して、
マルチスレッド実行環境を作ってみる。
他のバンドラーではうまくいかなかったのよねぇ。
Viteはjs上でWorkerロードできなかった。
rollup.jsは後書きで

あと、TypeScriptはデフォルトで使っちゃおう。
もう、素のJS使う人なんておらんやろ。

ヾ(・ω<)ノ" 三三三● ⅱⅲ コロコロ♪

------------------- ↓ 本題はここから ↓-------------------

RustNode.jsのインストール

過去記事を参考のこと
以下のコマンドが動くようにしておく

node --version
  v16.4.2
npm --version
  7.17.0
cargo --version
  cargo 1.54.0 (5ae8d74b3 2021-06-22)

cargoとnpmコマンドでプロジェクト作成

cargo newにてcargoプロジェクトを作成する
今回はいろいろ省略するためにwasm-packデフォルトテンプレートを使用

cargo install cargo-edit wasm-pack wasm-bindgen-cli
wasm-pack new parcel-worker
cd parcel-worker
npm init -y
npm i -D parcel rimraf http-server typescript @parcel/transformer-typescript-tsc @parcel/validator-typescript
npx tsc -t ES2021 -m ES2020 --allowJs --init
wasm-pack build --release --target web --out-name parcel-worker

Parcelの設定を調整

設定不要が売りのParcelだが、
TypeScript使うときは内容を調整した方が使いやすい。

./.parcelrc
{
  "extends": "@parcel/config-default",
  "transformers": {
    "*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
  },
  "validators": {
    "*.{ts,tsx}": ["@parcel/validator-typescript"]
  }
}

package.jsonも調整

package.json
- "main": "index.js",

メインスレッドを設置

スレッドを二つ生成してwasmを実行する

./src/index.ts
(async () => {
    const path =  new URL('./worker.ts', import.meta.url)
    let worker = []
    for(let i = 0; i < 2; i++) {
        worker[i] = new Worker(path, {type:"module"});
    }
})();

worker側を設置

JS-wasm間で文字列と配列のやり取りできるか確認する

./src/worker.ts
import init, {greet, sum_numbers} from '../pkg/parcel-worker.js'

(async () => {
    await init()
    console.log(greet("dozo"))
    console.log(sum_numbers(new Int32Array([5,60,300])))
})()

フロントエンドを用意

./index.html
<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
    <title>My awesome Rust, WebAssembly, and Parcel application</title>
    <script type="module" src="./src/index.ts"></script>
  </head>
  <body>
  </body>
</html>

実装してビルド

今回は文字列の取得と、配列の合計を取得

./src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn sum_numbers(slice: &[i32]) -> i32 {
    slice.iter().sum()
}

#[wasm_bindgen]
pub fn greet(s: &str) -> String {
    format!("Hello {}!", s)
}

ビルド開始

wasm-pack build --release --target web --out-name parcel-worker
npx parcel serve ./index.html

http://localhost:1234 にアクセス

console.logに以下の文字列がでてるはず

Hello dozo!
worker.ts:6 365

サーバー起動

npx http-server ./dist

http://172.21.150.239:8080 にアクセス

スレッドの確認

メインスレッドとは別に二つスレッドが立っていることがわかる


スレッド

結果の確認

文字列、計算ともにやり取りができていることがわかる

(^_^;) やっとベースができたか

10回以上hello world記事を書いているわけだが、
漸くベースが出来上がった気がする。

------------------- ↓ 後書はここから ↓-------------------

ビルドターゲット"no-modules"は?

手動でデプロイディレクトリに置くのなら可能かもだが、
ビルドシステムに載せるのは無理っぽい。

rollup.jsなら?

Parcelだと最小行程でいけるけど、
ビルド後の挙動が不安定な感じがする。
(Parcel serveコマンドだと安定するんだけどね)

rollup.jsで行きたいところだが、
ビルド環境を構築できずにいる。
確立した時点で別記事にするかも。

SyntaxError: Unexpected token 'export' エラー

普通にビルドすると以下のエラーが出た

❯ npx parcel watch ./index.html --no-cache
🚨 Build failed.

Error: Unexpected token 'export'

  /home/dozo/Repos/parcel-worker/node_modules/@parcel/ts-utils/src/index.js:2
  export * from './FSHost';
  ^^^^^^
  
  SyntaxError: Unexpected token 'export'
SyntaxError: Unexpected token 'export' エラー全文
❯ npx parcel watch ./index.html --no-cache
🚨 Build failed.

Error: Unexpected token 'export'

  /home/dozo/Repos/parcel-worker/node_modules/@parcel/ts-utils/src/index.js:2
  export * from './FSHost';
  ^^^^^^
  
  SyntaxError: Unexpected token 'export'
  at Object.compileFunction (node:vm:352:18)
  at wrapSafe (node:internal/modules/cjs/loader:1031:15)
  at Module._compile (node:internal/modules/cjs/loader:1065:27)
  at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
  at Module.load (node:internal/modules/cjs/loader:981:32)
  at NodePackageManager.load (/home/dozo/Repos/parcel-worker/node_modules/@parcel/package-manager/lib/NodePackageManager.js:163:9)
  at NodePackageManager.requireSync (/home/dozo/Repos/parcel-worker/node_modules/@parcel/package-manager/lib/NodePackageManager.js:129:17)
  at Module.m.require (/home/dozo/Repos/parcel-worker/node_modules/@parcel/package-manager/lib/NodePackageManager.js:149:19)
  at require (node:internal/modules/cjs/helpers:94:18)
  at Object.<anonymous> (/home/dozo/Repos/parcel-worker/node_modules/@parcel/transformer-typescript-tsc/lib/TSCTransformer.js:10:16)

@parcel/transformer-typescript-tsc にバグがあったようで、
2.0.0-alpha.3 の段階で発生
nightlyでは修正済みとあるので、
次のバージョンである 2.0.0-rc.0では修正されている
(stableの段階では考えなくてもいい)

https://github.com/parcel-bundler/parcel/issues/4627

以下のコマンドでバージョンアップしておく

npm i -D @parcel/transformer-typescript-tsc@next @parcel/validator-typescript@next

参考:
Parcel2(Parcel@next)を使ってTypescript + SCSS環境を構築する

Discussion