今回は概要の概要みたいな部分しか扱いません.歴史や詳しい話などを知りたい方は 参考文献 に示したページを参照してください(特に MDN は何もかもが載っているのでおすすめです.)
WebAssembly はモダンなウェブブラウザーで実行できる新しいタイプのコードです。ネイティブに近いパフォーマンスで動作するコンパクトなバイナリー形式の低レベルなアセンブリ風言語です。(WebAssembly | MDN より引用)
従来では,ブラウザ上でプログラムを動作させるためには JavaScript を記述するしかありませんでした.JavaScript には長い歴史があり,今でこそ柔軟性や表現量に優れている高水準な言語となりました.しかし,C/C++ や Rust といったコンパイル言語と比較してパフォーマンスが悪いという問題点はおそらくこれからも解決しないでしょう.それを解決するのが WASM です.
WASM はプログラミング言語とかライブラリとかではなく,ブラウザにおけるバイナリコードの新しいフォーマット です.以下のような特徴を持ちます.
これにより,以下のことが可能になります.
これらの点から,近年では WASM が注目され初めています.
今回は単純に WASM のパフォーマンスの高さを知ろうということで,入力した整数以下の素数を全列挙する Web アプリケーションを制作してみます.
まずは肩慣らしとして JS から Rust で実装した fn add(a: i32, b: i32) -> i32;
を呼び出してみましょう.
準備
適当なディレクトリで以下のコマンドを実行し,warming-up
に移動してエディタを開いてください.
$ rustup target add wasm32-unknown-unknown
$ cargo new --lib warming-up
コード
src/lib.rs
// rustc はコンパイル時にデフォルトで名前修飾(mangle)をするので,
// attribute を指定することによって mangle しないようにコンパイラに伝える
#[no_mangle]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Hello, WebAssembly!</title>
<script>
const wasm = "./target/wasm32-unknown-unknown/release/warming_up.wasm";
fetch(wasm) // wasm ファイルを fetch(読み込み) する
.then((response) => response.arrayBuffer()) // 読み込んだ結果をバイト列に変換
.then((bytes) => WebAssembly.instantiate(bytes, {})) // wasm のバイト列から wasm のインスタンスを生成
.then((results) => {
console.log(results.instance.exports.add(1, 2)); // exports の中に rust で定義した関数が入っているので,そこから add を呼び出し
});
</script>
</head>
</html>
Cargo.toml
[lib]
crate-type = ["cdylib"]
実行
# rust を wasm にビルド
$ cargo build --target=wasm32-unknown-unknown --release
# 簡単な http サーバーを建てる
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
http://localhost:8000 にアクセスし,開発者ツールを開いて console を除くと 3
と表示されていると思います.これが wasm の add() を呼び出した結果となります.
現状,add の引数がハードコーディングされているので少し味気ないですね.そこで,ブラウザ上から整数を2つ入力し,それを add 関数に渡すようにしてみましょう.
コード
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Hello, WebAssembly!</title>
<script>
const wasm = "./target/wasm32-unknown-unknown/release/warming_up.wasm";
function calcbtn_handle() {
const x = document.getElementById("input_x").value;
const y = document.getElementById("input_y").value;
const res = add(parseInt(x, 10), parseInt(y, 10)).then((x) => {
document.getElementById("result").textContent = x;
});
}
function add(x, y) {
return fetch(wasm) // wasm ファイルを fetch(読み込み) する
.then((response) => response.arrayBuffer()) // 読み込んだ結果をバイト列に変換
.then((bytes) => WebAssembly.instantiate(bytes, {})) // wasm のバイト列から wasm のインスタンスを生成
.then(
(results) => results.instance.exports.add(x, y) // exports の中に rust で定義した関数が入っているので,そこから add を呼び出し
);
}
</script>
</head>
<body>
<label>数値を入力してください.</label>
<div>
<label>x: </label>
<input type="number" id="input_x" />
</div>
<div>
<label>y: </label>
<input type="number" id="input_y" />
</div>
<div>
<button onclick="calcbtn_handle();">calc</button>
</div>
<div id="result"></div>
</body>
</html>
実行
以下のようにして整数を2つ入力し,calc ボタンを押すこと色々な計算ができるようになったと思います.
ということでウォーミングアップが終わったので本題の方を進めていきましょう.
…とはいえ,ウォーミングアップで大体やりたいことはやったので,後は add 関数を sieve
関数にするだけです.
準備
また適当なディレクトリに移動して以下のコマンドを実行してください.
今回は npm を用います.
$ npm init rust-webpack sieve-wasm
$ cd sieve-wasm
$ npm install react react-dom # 私は素の js が書けません
コード
src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
extern "C" {
fn alert(s: &str);
}
/// \[1, n\] 内の素数を列挙して alert に渡す \
#[wasm_bindgen]
pub fn sieve(n: usize) {
let mut prime_table = vec![]
for i in 3..=((n as f64).sqrt() as usize) {
if !prime_table[i] {
continue;
}
let mut j = 2 * i;
while j <= n {
prime_table[j] = false;
j += i;
}
}
let mut prime_list = Vec::new();
for (i, is_prime) in prime_table.iter().enumerate() {
if *is_prime {
prime_list.push(i);
}
}
let primes_str = prime_list
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()
.join(" ");
alert(&primes_str);
}
js/index.js
import("../pkg/index.js").then((module) => {
module.sieve(10000);
})
function sieve(n) {
let prime_table = new Array(n + 1);
prime_table.fill(true);
prime_table[0] = false;
prime_table[1] = false;
console.log(Math.sqrt(n));
for (let i = 2; i <= Math.sqrt(n); i++) {
if (!prime_table[i]) continue;
let j = i * 2;
while (j <= n) {
prime_table[j] = false;
j += i;
}
}
let primes = new Array();
for (let i = 2; i <= n; i++) {
if (prime_table[i]) {
primes.push(i);
}
}
alert(primes.join(" "));
}
WASM は JS よりも速い!
2022年2月23日 静岡大学プログラミングサークル SZPP earlgray(@earlgray329) 0. 準備編 2022年2月23日(水)の現時点で、まだ Go1.18 はリリース前の RC 版でしか提供されていません。 今回は goenv とか特に使わずに /usr/local/go/bin/go に RC 版をインストールしますが、stable じゃないと嫌という方は docker 使うなりしてください。 インストール(Unix/Linux)
Mar 30, 2022インストール $ echo `uname`_`arch` # OS と Arch を確認 Linux_amd64 $ curl -OL https://go.dev/dl/go1.17.5.linux-amd64.tar.gz $ sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.17.5.linux-amd64.tar.gz $ echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile
Jan 16, 2022AtCoder の DP 問題 EDPC Dynamic Programming - AtCoder Tags DP のこころへ dp テーブルの添字が何を示すのかを明確にしろ dp テーブルがどのように遷移していくのかを明確にしろ
Nov 6, 2021おまえだれ あーるぐれい(@earlgray329)と申します。 普段は Rust で競技プログラミングをやったり(AtCoder緑, CafeCoder無色)、Golang でサーバーを書いたりしています。サーバーサイドはまあまあやったつもりなので、そろそろ React でフロントを書いてみたいお気持ちなのですが、課題が山積みで手がつけずの状態です。 予備自衛官補って何? さて本題に入ります。  まずタイトルみても「予備自衛官補ってなんだ?」としかならないと思いますが、読んで字の如く「予備の自衛官の補」です(適当)。まあ要するに自衛官のバイトの試採用ってことですね。自衛官のバイト(笑)って思っているそこのあなた、侮ってはいけません。自衛官のバイトも訓練内容はほとんど本職の自衛官と全く変わりません。腕立て伏せはしますし、ランニングもしますし、そしてもちろん実銃の射撃訓練だってします。面白そうですよねいや大変ですよね〜・・・。  という感じで、予備自衛官補は自衛官のバイトでもありながらかなり真面目に訓練はします。 どうやってなるの?
Dec 11, 2020or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up