LoginSignup
23
2

More than 1 year has passed since last update.

正規表現の例として、WebAssemblyとGopherJSを比較する

Last updated at Posted at 2022-12-07

この記事は Wano Group Advent Calendar 2022 の8日目の記事です。

概要

GoをWebブラウザ上で動作させるために、色々な方法やツールはありますが、今回は正規表現のパターン判断を作るの例として、WebAssemblyとGopherJS二つのやり方で紹介と比較したいと思います。

WebAssemblyとGopherJS

  • WebAssembly(略称Wasm)は、Webブラウザを含むモダンな実行環境での効率的なコード実行ために、アセンブリ言語ライクなテキスト形式で設計された低レベルフォーマットだ。GO言語使いたい場合、公式のリポジトリからwasmファイルをロードするツール(wasm_exec.js)を提供します

  • 一方、GopherJSはGoのコードをJavaScriptに変換するライブラリです。そのほか、コンパイラ以外の機能もCLIで提供。

GOの実装

今回は簡単な正規表現(英数のみの文字を判断)を例として実装します。

WebAssembly:

golang / wasm_main.go
//go:build wasm
// +build wasm

package main

import (
	"regexp"
	"syscall/js"
)

// 正規表現の共通関数
func IsStringOnlyIncludeAlphaNum(this js.Value, args []js.Value) interface{} {

	var ValueCheck = regexp.MustCompile("^[0-9a-zA-Z_]+$").MatchString
	return ValueCheck(args[0].String())

}

func main() {
	js.Global().Set("IsWasmStringOnlyIncludeAlphaNum", js.FuncOf(IsStringOnlyIncludeAlphaNum))
	select {} // avoid exit
}

compileしてwasmの生成
GOOS=js GOARCH=wasm go build -o main.wasm wasm_main.go

次はGopherJSのGO実装:

golang / gopherjs_main.go
//go:build js
// +build js

package main

import (
	"github.com/gopherjs/gopherjs/js"
	"regexp"
	sysjs "syscall/js"
)

// 正規表現の共通関数
func IsStringOnlyIncludeAlphaNum(this sysjs.Value, args []sysjs.Value) interface{} {

	var ValueCheck = regexp.MustCompile("^[0-9a-zA-Z_]+$").MatchString
	return ValueCheck(args[0].String())

}

func main() {
	js.Global.Set("IsGopherStringOnlyIncludeAlphaNum", sysjs.FuncOf(IsStringOnlyIncludeAlphaNum))
}

gopherjsのコマンドがパッケージをビルドして生成する
gopherjs build -o gopherjs_test.js gopherjs_main.go

フロント側

bodyだけ表示

<body>
    <h1>WebAssemblyの場合</h1>
    <script src="./wasm_exec.js"></script>
    <script>
        const go = new Go()
        WebAssembly.instantiateStreaming(fetch('main.wasm'), go.importObject).then(result => {
            go.run(result.instance);
        })
        async function run() {
            const text = document.getElementById("testStr").value;
            console.log("文字が英数のみ:"+self.IsWasmStringOnlyIncludeAlphaNum(text));
        }
    </script>
    <input type="text" id="testStr" name="testStr">
    <button onClick='run()' id="buttonCheck">確認</button>
</body> 
<body>
    <h1>GopherJSの場合</h1>
    <script src="./gopherjs_test.js"></script>
    <script>
        async function run() {
            const text = document.getElementById("testStr").value;
            console.log("文字が英数のみ:"+self.IsGopherStringOnlyIncludeAlphaNum(text));
        }
    </script>
    <input type="text" id="testStr" name="testStr">
    <button onClick='run()' id="buttonCheck">確認</button>
</body>

HTTPサーバを起動する (両方共通)

server.go
package main

import (
	"fmt"
	"log"
	"net/http"
)

func main() {
	fmt.Print("start server...")
	http.Handle("/", http.FileServer(http.Dir("")))
	log.Fatal(http.ListenAndServe(":8080", nil))
}

$ go run server.go

結果表示

スクリーンショット 2022-11-30 0.45.12.png
スクリーンショット 2022-11-30 0.46.57.png

両方の速度比較

performanceを使って確認

        async function run() {
            const text = document.getElementById("testStr").value;
            let startTime1 = performance.now();
            self.IsGopherStringIncludeAlphaNum(text);
            let startTime2 = performance.now();
            const go = new Go()
            WebAssembly.instantiateStreaming(fetch('main.wasm'), go.importObject).then(result => {
                go.run(result.instance);
                self.IsStringIncludeAlphaNum(text);
                let startTime3 = performance.now();
                console.log("GopherJS (キャッシュなし): ",startTime2-startTime1);
                console.log("wasm(インスタンス化時間を含め): ",startTime3-startTime2);
                let startTime4 = performance.now();
                self.IsStringIncludeAlphaNum(text);
                let startTime5 = performance.now();
                self.IsGopherStringIncludeAlphaNum(text);
                let startTime6 = performance.now();
                console.log("wasm: ",startTime5-startTime4);
                console.log("GopherJS: ",startTime6-startTime5);
            })
        }

テスト環境 : Chrome
バージョン : 107.0.5304.121(Official Build) (x86_64)

項目 / 文字サイズ 10byte 200kb 1MB 10MB
Wasm 0.20ms 1.39ms 9.79ms 102.69ms
GopherJS 1.29ms 1.80ms 5.70ms 39.10ms
Wasm (インスタンス化時間を含め) 23.80ms 38.60ms 46.79ms 1951.80ms
GopherJS (キャッシュなし) 9.10ms 10.79ms 12.60ms 46.09ms

まとめ

複雑な計算処理に関しては、Wasmの処理速度が早いですか、文字処理など通常のWeb開発の場合、GopherJSでも劣らないと思います。

23
2
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
23
2