4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

イントロ

WebAssembly... とても速そうな響きである。
近年、HTML、CSS、JavaScriptに続きウェブ上で動作する新たな言語として注目されている。
WebAssembly、WasmはC++やRustからコンパイルし、JavaScriptよりも速く動作することを目的としている。

しかしどうやら2023年年末現在、どうやらWasmはDOMを直接扱えないらしい。WebGLなどのCanvasの描画にはWasmが使われるらしいがDOM自体となるとそうもいかないらしい。

とはいえフロントがカクついたりすると(書き方のせいであろうが)何かもっと動かないものだろうかと思ってしまう。

いや、オーバーヘッドがデカいだけならWasmでHTMLを書いてinnerHTMLにぶん投げれば速いのでは?

ここではRustを使ってWasmをやってみた。

Wasmのビルド

RustはMozzilaがやっているのでMDNを見ればわかりやすく書いてある。

  1. wasm-pack のインストール
    wasmをビルドするためにcargoでツール wasm-pack を取ってくる。

    cargo install wasm-pack
    
  2. wasm-bindgen の追加
    JavaScriptとRustをつなぐための wasm-bindgen というクレートを追加する。

    cargo add wasm-bindgen
    
  3. ビルド
    いつもの cargo build ではなく wasm-pack を使ってビルドする。

    wasm-pack build --target web
    

    cargo build ではバイナリが target 下に出力されるが、 webで使うものは pkg に出力される。

    package.json などもいるので他のディレクトリから npm install './pkg' などで見に行けば .d.ts も吐き出してくれているし、簡単に使えるだろう。

innerHTMLする

Wasm

シンプルに以下のようなくそでかテーブルをinnerHTMLしよう

// lib.rs -> table_string.js
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn create_table() -> String {
    let mut table = String::new();

    table.push_str("<table><tbody>");

    const SIDE: i32 = 256;

    for i in 1..SIDE {
        table.push_str("<tr>");
        for j in 1..SIDE {
            table.push_str(&format!("<td>{}</td>", (i - 1) * SIDE + j));
        }
        table.push_str("</tr>");
    }

    table.push_str("</tbody></table>");

    table
}

MDNのように呼び出し側は

<head>
	<script type="module" src="./main.js"></script>
</head>
<body>
	<div id="contents"></div>
</body>
'use strict';

import init, { create_table } from '../../rust/pkg/table_string';

const main = async () => {
    const contents: HTMLDivElement = document.getElementById('contents') as HTMLDivElement;

    init().then(() => {

        let ave = 0;
        for(let i = 0; i < 100; i++){

            const start: number = performance.now();
            const t = create_table();
            contents.innerHTML = t;
            const end: number = performance.now();

            const pastTime = end - start;
            console.log(`Time: ${pastTime}`);
            ave += pastTime;
        }

        console.log(`average: ${ave / 100}`);
    });
    return;
};

window.addEventListener('load', main);

とした。viteでbundleしたのでmain.jsだけとなっているが.wasmはimportされるので type="module" が必要。

大きなテーブルの完成である。
image.png

256x256のテーブルを作成し、100回の平均を取ったが速度は私の環境では
image_wasm.png

average: 140

となった。
速いのか? 比較せねばなるまい。

TypeScript

ここでも愚直にStringを作成しinnerHTMLを行った。

const create_table_by_inner_html = (): string => {
    const SIDE = 256;
    let table = '<table><tbody>';
    for (let i = 1; i < SIDE + 1; i++) {
        table += '<tr>';
        for (let j = 1; j < SIDE + 1; j++) {
            table += `<td>${(i - 1) * SIDE + j}</td>`;
        }
        table += '</tr>';
    }
    table += '</tbody></table>';

    return table;
};

結果はこちら

image_jsstr.png

average: 131

あまり速くない。というか全く速くない。

オーバーヘッドを考えれば少しは速いのかもしれないがオーダー的にメリットはないだろう。

innerHTMLは遅い

よく言われることであるが innerHTMLは遅いらしい。
DOMでやってみようか。
何回も createElement したり威圧感としてはNo.1だろう

const create_table_by_dom = (): HTMLTableElement => {
    const SIDE = 256;

    const table: HTMLTableElement = document.createElement('table') as HTMLTableElement;
    const tbody: HTMLTableSectionElement = document.createElement('tbody') as HTMLTableSectionElement;

    for (let i = 1; i < SIDE + 1; i++) {
        const row: HTMLTableRowElement = document.createElement('tr') as HTMLTableRowElement;
        for (let j = 1; j < SIDE + 1; j++) {
            const data: HTMLTableCellElement = document.createElement('td') as HTMLTableCellElement;
            data.innerText = `${(i - 1) * SIDE + j}`;
            row.appendChild(data);
        }
        tbody.appendChild(row);
    }

    table.appendChild(tbody);

    return table;
};

結果は...

image_jsdom.png

average: 123

やっぱり...見かけによらずこれが一番速いんですね...

まとめ

複雑なパースを行ったりなどしてDOMをつくるならもしかしたら速いのかもしれないが、
やはりDOMはWasmで触るものではないらしい。

画像処理やレンダリングなどオーバーヘッドが気にならない、DOM操作以外ならばWasmを使うなど適材適所使い分けていきたいですね。

WasmがDOMを直接操作できるようになるほうが嬉しいのではあるが。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?