焼売飯店

RubyとかJSとか

Goの標準パッケージのwasmサイズを計測した

Go 1.11でgo buildのtargetに

  • GOOS=js
  • GOARCH=wasm

を指定できるようになって久しいですが、皆さんこちらお使いでしょうか?

Goが出力するWebAssemblyのファイルサイズが気になり、 これを計測するためのリポジトリgo-wasm-sizesを作ったので、こちらと合わせて実際の計測結果を紹介します。

github.com

wasmのサイズ計測

やっている事は単純で、

  • Goの標準パッケージのリストを書き出す
  • それらをimportだけして使用しないコードを生成する
  • 全てwasmにビルドする

作業を行っているだけです。

コード例

package main

import _ "fmt"

func main() {}

また、比較対象として、何のpackageもimportしていないパターンも用意しました。

計測方法について追記 (2018/12/12)

渋川さんからご指摘いただきましたが、今回の計測方法だとdead code eliminationされている可能性がありそうなので、こちらも考慮した上で別途検証したいです。

計測結果

よく使いそうなpackageを適当に選んで紹介します。 計測対象にしている全packageのリストは、上記リポジトリのREADME.mdに記載があります。

※net/httpなどの子packageは、作業の簡略化のために / ではなく _ で連結した名前を設定しています。

1.30MB blank/blank.wasm # This package only contains func main().
1.30MB math/math.wasm
1.30MB strconv/strconv.wasm
1.31MB io/io.wasm
1.34MB syscall_js/syscall_js.wasm
1.43MB time/time.wasm
1.44MB unicode/unicode.wasm
1.45MB strings/strings.wasm
1.45MB bytes/bytes.wasm
1.45MB path/path.wasm
1.46MB bufio/bufio.wasm
1.50MB os/os.wasm
1.98MB reflect/reflect.wasm
1.98MB sort/sort.wasm
2.00MB regexp/regexp.wasm
2.15MB fmt/fmt.wasm
2.16MB net_url/net_url.wasm
2.16MB context/context.wasm
2.37MB net/net.wasm
7.22MB net_http/net_http.wasm
11.32MB net_http_pprof/net_http_pprof.wasm

わかったこと

  • 何もimportしなくてもwasmのサイズは1.3MBになり、一般的なJSファイルと比べるとかなり大きい。
  • fmtをimportするとサイズが +800KB となり意外と大きく(恐らく内部でreflectを使っているため)、手軽に使うには向かない。
    • builtinprintln() が JSコンソールに表示を行うので、Goで実行した内容をコンソールに出したいだけであればそちらを使うのがよさそう。
  • strconv, math等はほとんどサイズに影響を及ぼさない。何もimportしない状態で既にビルドに含まれているのかも?

一番大きかったpackageは net/http/pprof で、なんと 11.32MB になりました。

圧縮するとどうなる?

生のままだとファイルサイズが大きすぎるため、実際にGoのwasmを使用する場合は、ブラウザがサポートする形式で圧縮して送信するのが現実的な方法となりそうです。

今回は、Gzip, Brotliの2つで、圧縮結果のサイズを比較してみました。

Gzip

287KB blank/blank.wasm.gz # This package only contains func main().
287KB math/math.wasm.gz
287KB strconv/strconv.wasm.gz
290KB io/io.wasm.gz
295KB syscall_js/syscall_js.wasm.gz
307KB time/time.wasm.gz
322KB unicode/unicode.wasm.gz
323KB os/os.wasm.gz
324KB strings/strings.wasm.gz
324KB bytes/bytes.wasm.gz
324KB path/path.wasm.gz
325KB bufio/bufio.wasm.gz
414KB reflect/reflect.wasm.gz
414KB sort/sort.wasm.gz
418KB regexp/regexp.wasm.gz
444KB fmt/fmt.wasm.gz
445KB net_url/net_url.wasm.gz
446KB context/context.wasm.gz
497KB net/net.wasm.gz
1617KB net_http/net_http.wasm.gz
2455KB net_http_pprof/net_http_pprof.wasm.gz

Brotli

222KB blank/blank.wasm.br # This package only contains func main().
222KB math/math.wasm.br
223KB strconv/strconv.wasm.br
224KB io/io.wasm.br
228KB syscall_js/syscall_js.wasm.br
237KB time/time.wasm.br
247KB unicode/unicode.wasm.br
249KB os/os.wasm.br
249KB strings/strings.wasm.br
249KB bytes/bytes.wasm.br
249KB path/path.wasm.br
250KB bufio/bufio.wasm.br
312KB reflect/reflect.wasm.br
313KB sort/sort.wasm.br
316KB regexp/regexp.wasm.br
336KB fmt/fmt.wasm.br
336KB net_url/net_url.wasm.br
336KB context/context.wasm.br
375KB net/net.wasm.br
1768KB net_http_pprof/net_http_pprof.wasm.br

わかったこと

最小のblank.wasm, 最大のnet_http_pprof.wasmでそれぞれ比較すると、

blank.wasm

  • Gzip: 22% (287KB)
  • Brotli: 17% (222KB)

net_http_pprof.wasm

  • Gzip: 22% (2455KB)
  • Brotli: 16% (1768KB)

となっており、どちらの圧縮法を使っても20%程度にはなるようです。 これだけファイルサイズが変わるのであれば、体感のロード時間は大きく変わるので、やはりGoのwasmの配信は圧縮した上で行うのが基本的な方針になりそうです。

Go + wasmで実際に何か作られる際にぜひ参考にしてください!