WebAssembly: 「なぜ」と「どうやって」 [翻訳記事]

nabbisen - Aug 29 '18 - - Dev Community

本記事は、以下の記事の翻訳です:
WebAssembly: How and why by Milica Mihajlija on LogRocket
* 執筆者に許諾を頂いた上で掲載しています。


  • どのようにしてブラウザでネイティヴ・コードを実行するのか
  • そこにはどのような背景があるのか
  • それは JavaScript にとって、さらには Web 開発にとって、いったい何を意味するのか

すべてのブラウザにおいて、すなわち Chrome / Firefox / Edge / Safari いずれにおいても、コードは JavaScript エンジンによって解釈されて実行されます――ただ JavaScript が実行されるだけです。
残念なことに、JavaScript は、実行したいあらゆる処理にとって理想的なものというわけではありません。
ここに WebAssembly の入り込む余地があります。

WebAssembly とは、モダンなブラウザで実行可能な、新しいタイプのコードのことです。
Web のパフォーマンスをより良くするために開発されました。
低レベルなバイナリであり、容量が小さく、そのためロードから実行までが高速です。
WebAssembly は、それ自体を記述するのでは無く、他の高級言語からコンパイルして作成します。

アセンブリとは、人間が読め、且つ、機械コードに近い言語の典型です。
機械コードとは、プロセッサが理解可能な数字の羅列のことです。

アセンブリ言語と機械コード

あらゆる高級プログラミング言語は、プロセッサ上で動くように、機械コードへと翻訳されます。
プロセッサ・アーキテクチャの種類が異なれば、異なる機械コード、およびそれぞれに対応する異なるアセンブリが必要になります。

ソースコードは異なるプロセッサ・アーキテクチャに対応するようにそれぞれコンパイルされる

その名前に反して、WebAssembly は 本来の意味でのアセンブリ言語ではありません。
特定のマシン(機械)に対応したものでは無いからです。ブラウザに対応したものです。
コードがブラウザで実行される時、実際にどの種のマシン上で実行されるのかはわかりません。

コンパイル・ターゲットへの仲介者としての WebAssembly

WebAssembly は現実世界で広く使われているハードウェアの最小公倍数を表す概念マシンのための言語です。
ブラウザが WebAssembly のコードをダウンロードすると、そのコードはいかなるマシンのアセンブリにも速やかに変換されます。

こちらが WebAssembly の姿です――可読性の高いテキスト形式の姿(.wat)を持っていますが、実際にブラウザに移送される時にはバイナリ形式の姿(.wasm)を取ります。

WebAssembly のテキスト形式とバイナリ形式

WebAssembly によって、 C / C++ / Rust のコード類を取り扱えるようになり、 WebAssembly モジュール と呼ばれるものにコンパイルできるようになります。
これらは、Web アプリケーションにロードして、JavaScript から呼出すことができます。

WebAssembly は JavaScript の代替では無くパートナなのです。

アプリケーションにおける WebAssembly モジュール

なぜ WebAssembly が必要なのか

ブラウザ以外のソフトウェアを使用する必要がある場合を考えてください:
テレビゲーム / 動画編集 / 3D レンダリング / 音楽作成 などの場合が考えられます。
これらのアプリケーションは、大量の演算を行うため、高度のパフォーマンスが必要になります。
この種のパフォーマンスを JavaScript で達成することは困難です。

JavaScript はシンプルなスクリプト言語として登場しました。軽量なハイパーテキスト文書しか無かった頃の Web において、双方向通信を実現することが目的でした。
容易に学べて記述できるように設計されていましたが、高速で実行するようには設計されていませんでした。
何年も後になってから、 JavaScript の解釈 時に実行時最適化するための機能がブラウザに追加されました。これによってパフォーマンスは著しく改善されました。

JavaScript が速くなるにつれて、ブラウザで実行できる事柄が増えて来ました。
新しい API がつくられて、対話型の画像処理 / 動画ストリーミング / オフライン・ブラウジングやその他たくさんのことができるようになりました。
続々と、それまではネイティヴなアプリケーションでしか実現できなかったリッチな処理が、Web で実現できるようになったのです。
今日では、ブラウザから文書を編集してメールで送信するということが簡単にできます。
しかしなお JavaScript のパフォーマンスでは難解であることも残っています。

テレビゲームは特に挑戦しがいのある試みです。というのも音声処理や映像処理だけで無く、しばしば物理学や人工知能とも組み合わせなくてはならないからです。
Web 上で効率的にゲームを動かすに足るパフォーマンスを発揮することができるようになれば、 Web 上で他の多くのアプリケーションを実現できるように なるでしょう。
WebAssembly が成し遂げようとしているのはこの点です。

なぜ Web はかくも魅力的なのか

Web のすばらしさは、それが ✨魔法✨ のようであることです――どこででも動くのです。
ダウンロードもインストールも必要ありません。
必要になった時に 1 クリックすれば、ただちに Web アプリケーションはこちらにやって来ます。
このしくみは、コンピューターにバイナリをダウンロードして直接実行するよりも、安全です。
なぜならブラウザは、コードが実行された時にあなたのシステムがめちゃくちゃにされように、 セキュリティ性 を念頭に置いて設計されているからです。
そして Web では、何かを共有することは、何かを得るのと同じように、簡単に実現できます――リンクはどこにでも持ち運べる文字列です。

Web は、アプリケーションをあらゆるデバイスに対して適用可能にする、唯一の真に普遍的なプラットフォームです。
私たちは単一のコードベースをメンテナンスするだけで良くなります。アップデートはシンプルなタスクになります。さらには、すべてのユーザーがアプリケーションにアクセスできると見込めるようになります。

これらの内包されたパワーと Web がもたらす相方向性を背景に持ちつつ、私たちはハイパーテキストと小規模のスクリプト言語から出発しました。そして、はるかな旅路の果てに、驚くべきアプリケーションと可能性に富む、とてつもなくパワフルで広く普及したプラットフォームを手に入れたのです。
しかし今日に至るまで、その根源的な基盤は、これらの内の何かを第一義の目的として設計されたわけでは決して無い、一つのスクリプト言語でした。

WebAssembly がもたらす恩恵

WebAssembly を特別で Web にまさしくふさわしいものにしているのは、次の点です:

  • スピード
  • 移植性
  • 柔軟性

WebAssembly は スピード にフォーカスして設計されました。
そのバイナリのファイルサイズは、テキスト形式の JavaScript ファイルと比べると、非常に小さいです。
サイズの恩恵があるがゆえに、ダウンロードを早く行えます。このことは、遅いネットワークにおいて特に重要です。

WebAssembly のバイナリは、デコードから実行までのスピードも速いです。
JavaScript は動的型付け言語ですが、変数の型は事前に決めておく必要は無く、且つ、事前にコンパイルしておく必要もありません。
このため、記述する上では、簡単に速やかに行うことができます。
しかし同時に、JavaScript エンジンが、実行時に、非常に多くの仕事をしなければならなくなっています。
ページ上で動作する時に、パースして、コンパイルして、コードの実行時最適化を、行わなければなりません。

JavaScript をパースするというのは、プレーンなテキストを 抽象構文木 (AST)と呼ばれるデータ構造に変形して、それをバイナリ形式に変換するということです。
WebAssembly では、移送はバイナリ形式で行われます。その後にデコードされることでさらに高速になります。
JavaScript と異なり、静的型付け言語ですので、エンジンはコンパイル時にどの型が使われているのかということを気にしなくても良いです。
大半の実行時最適化はソースコードのコンパイル時に行われます。ブラウザに到達さえしていない時に行われるのです。
メモリ管理は、C 言語や C++ 言語で行われるのと全く同様に、個別に行われます。ガベージ・コレクションは一切存在しません。
これらすべてのことが、より速くより安定したパフォーマンスをもたらします。
WASM バイナリの実行時間は、同じ処理をネイティヴ・コードで実行した場合に比べて、20% 増にとどまります。

JavaScript エンジンにおける WebAssembly の相対的な処理時間

WebAssembly 設計時の主要な目標の一つは 移植性 でした。
デバイスでアプリケーションを実行するためには、そのデバイスのプロセッサ・アーキテクチャとオペレーティング・システムに適合している必要があります。
すなわち、サポート対象とするオペレーティング・システムと CPU アーキテクチャのあらゆる組合せに適合するように、ソースコードをコンパイルできなければならないのです。
WebAssembly を用いれば、コンパイル手順を一つ踏むだけで、あなたのアプリはすべてのモダンなブラウザで実行できるようになります。

ネイティヴ・コードのコンパイル
複数のプラットフォームで動くようにするか vs. WebAssembly にするか

Web の世界に運び込めるものは、自作のアプリケーションだけではありません。
Web とは無関係のところにあった、既存の C++ のライブラリとオープンソースのアプリケーションという、とほうもない資産を、運び入れることもできるのです。
C++ は、iOS ならびに Android を含む、実用的なすべてのプラットフォームでサポートされている言語です。
WebAssembly を用いることで、C++ を Web にもモバイルにも配信できる共通言語として活用できるようになります。

WebAssembly に関して最もわくわくさせられるのは、より 柔軟性 をもって Web の記述ができるようになる、という点です。
今までは JavaScript が、Web ブラウザで完全にサポートされている唯一の言語でした。
WebAssembly があれば、Web 開発者は JavaScript 以外の言語を選ぶことができるようになります。さらには、より多くの開発者が Web のためのコードを書くことができるようにもなります。
JavaScript は、大半の開発においては、依然として最善の選択であり続けるでしょう。
しかしパフォーマンスアップが真に必要となった場合、ことと次第によっては、それに特化した言語を導入するという選択肢を検討することができるようになります。
UI やアプリのロジックのような部分は JavaScript でまかないつつ、コアな機能を WebAssembly で実装するということが可能になるのです。
既存の JS アプリにおいて、パフォーマンスの実行時最適化時にボトルネックとなるものがあれば、この問題の解決により適した言語でその部分を書き直すこともできます。

現在、WebAssembly を完全にサポートしている言語は C / C++ / Rust です。
しかし現在開発中の 他の候補 もたくさんあります。
この中には Kotlin / .NET も含まれます。両方とも既に実験的サポートが開始されています。

どのようにして動かすか

ソースコードを WebAssembly にコンパイルするためのツールが必要です。
一つの解決方法として、安定した、モジュール式のコンパイル・ツールチェインである LLVM を使うということが考えられます。複数の言語に対して動作するように構成されています。
C と C++ をコンパイルする場合は、 Emscripten と呼ばれる、LLVM をベースにした、よりシンプルなツールを使うこともできます。
Rust の Nightly 版では、組込みコンパイラである rustc を用いて、直接 WebAssembly を出力することができます。

C で "Hello world" を書いた時のことを考えてみましょう。
次の Emscripten のコマンドは、ブラウザ上で実行するために必要なファイル群を集めて来てくれます。
結果として、 HTMLJS ファイルとともに動く WebAssembly のモジュールが生成されます。

emcc hello.c -s WASM=1 -o hello.html
Enter fullscreen mode Exit fullscreen mode

Emscripten で C / C++ のコードを WebAssembly にコンパイルする

HTML と JS ファイルが必要である理由は、WebAssembly は、どのプラットフォームであっても、その API 群――DOM / WebGL / WebAudio など――に直接アクセスすることはできないからです。
これらの内の何かを利用するためには、それがたとえページに WebAssembly の実行結果を表示するだけであっても、JavaScript を経由して行う必要があります。
Emscripten は JS コードを生成しますが、それが モジュールの下準備をして Web API 群とのやり取りを可能にしてくれます。
HTML ファイルは、その JS のロードと、WebAssembly の出力内容の textarea または canvas 要素への表示を行ってくれます。

WebAssembly のバイナリは、通常のアプリのモジュールであると考えることができます:
ブラウザからフェッチでき、ロードでき、実行することができるのです。
インポートとエクスポートのしくみを持っていて、JavaScript のオブジェクトと同様に操作することができます。
JavaScript のコード内で WebAssembly の関数を呼び出すことができますし、WebAssembly のモジュール内で JavaScript の関数を呼び出すこともできます。

WebAssembly には 4 つのプリミティヴな型しかありません。
すべて数値型――整数型ならびに浮動小数点数型(i32 / i64 / f32 / f64)です。
このことは、JavaScript と WebAssembly の間で、より複雑なデータ型のやり取りをすることは簡単なことでは無い、ということを意味しています。
例えば文字列のやり取りをする場合には、数値の配列にエンコードしてから、そのポインタを渡す必要があります。
WebAssembly は自分の線形メモリの中でしか read も write もできません。
外部の JavaScript 変数に直接アクセスすることはできません。
そうするためには、それをメモリにコピーするかコールスタックで渡すかする必要があります。

今の段階では、JavaScript から大量の呼び出しを行うと、非常に遅くなります。
エンジンが毎回セットアップの処理を行うためです。
将来はこの仕様は変更されるかもしれません。
しかしいま時点では、WebAssembly を独立系で良く動くシステムと考えて、重量級の仕事を任せるために使用するのが、良さそうです。

もし何もセットアップを行わずに WebAssembly を試してみたいのでしたら、webassembly.studio もしくは WebAssembly Explorer を訪ねてみてください。

私にも使えますか?

はい!

WebAssembly は、現実の、手の届くところにあります。
昨年、すべての主要なブラウザで WebAssembly のサポート が実現しました。
現在において、全世界のユーザーの 74.93% がサポート対象となっています。デスクトップ・ユーザーに限れば 82.92% が対象です。
旧式のブラウザをカバーするための対応として、Emscripten で asm.js にコンパイルすることもできます――これは数値のみを扱う(文字列やオブジェクトなどを扱わない)JavaScript のサブセットです。
asm.js は WebAssembly の誕生に直接つながる手法であり、Web では広く使われています。例えば Facebook に写真をアップロードする時に画像を圧縮したり、Adobe の Lightroom で画像を編集したりする時に、使われています。

WebAssembly をサポートしているブラウザ

いまや現実世界には WebAssembly のとてもわくわくさせてくれる実例が存在します。

私はテレビゲームが WebAssembly の大きな目標になると言いましたが、Unity と Unreal Engine 4 の両方において、動くデモが既につくられています。
Unity エンジン上で WebAssembly を動かしている Tanks! (戦車)ゲームをプレイすることができます。
Epic 社が WebAssembly の短いオンラインデモ を公開しています。

Tanks! Demo — WebAssembly
これは Tanks! のデモです。
Unity のチュートリアルにあるゲームを WebAssembly にエクスポートしたものです。
砂漠の戦車を操縦してください...
webassembly.org

Figma はインタフェースの設計ツールです。
ブラウザ上で動き、設計者たちが一緒に仕事をして成果を共有することを容易にしています。
主として C++ で記述されていて、2D の WebGL レンダリングエンジンを用いることで大容量のドキュメントの制御を実現しています。
Figma の開発者たちは、最初は、asm.js を使って C++ のコードを Web 向けにコンパイルしていました。
それを WebAssembly に切り替えたことで、ドキュメントサイズによらず、ロード時間を 1/3 未満にまで改善できました。

WebAssembly cut Figma’s load time by 3x
WebAssembly のケーススタディ blog.figma.com

AutoCAD はデザインソフトです。
主に多様なエンジニアリング分野で利用されており、間取り図 / 電気回路 / 配管計画などを描画することができます。
C++ で記述されていて、35 年ほどの歴史を有しています。Web そのものよりも歴史は長いのです。
WebAssembly のおかげで、他の言語で書かれた膨大なコードベースを書き直すことを必要としないまま、Web アプリとして利用できるようになっています。

WebAssembly を活用した、もっともっとたくさんのアプリケーションが、出て来るでしょう。
オンライン上には、ブラウザで動く 動画編集ツール / レイトレーサ(3D 画像レンダラ) / 顔認識アルゴリズム のような、おもしろいデモも現れています。

来たるべきもの

ブラウザでは既に 新しい機能 の実現に着手しています。
スレッド管理ガベージコレクション のサポートが実装されつつあります。これによって WebAssembly は、Java / C# / Go のような言語のコンパイルターゲットとしてより適合したものになるでしょう。
重要な目標の中には、ソースマップのサポートを実装して デバッグツール を作成することも、含まれています。これによって開発者は WebAssembly をソースコードに簡単にマップさせられるようになるでしょう。

JavaScript はこれまで通り Web 開発の重要な地位を占め続けるでしょう。
偉大な言語であり、ほとんどあらゆるものを構築する上で、十分に柔軟です。
そして JavaScript がうまく扱えない数少ない隙間を WebAssembly が埋めて行き得るのです。
JavaScript を WebAssembly にコンパイルすることはできませんが、実際にはそのようなことができても大して意味をなしません。なぜならブラウザは、JS と直接やり取りをして、そのパフォーマンスを最大化するように、既に設計されているからです。

しかし仮に JavaScript のみで開発を続けるとしても、WebAssembly の恩恵とそれがもたらすスピードアップの効果は、ライブラリとフレームワークの改善を通して、変わり無く受けることができます。
待ち遠しさを感じることも無い内に、<script type='module'> を使用することで、他のあらゆる ECMAScript モジュールのように、これらのモジュールをダウンロードして インポート できるようになるでしょう。そして JavaScript からそれらの関数をシンプルに呼び出すことができるようになるでしょう。
フレームワークについてはどうかと言いますと、Ember は既に WebAssembly の Glimmer VM への実装方法を調査しています。 React の機能 のいくつかも WebAssembly で実装される可能性を秘めています。

来たるべき未来が今ここにあり、高速で動きながら輝いています🚀🌞

. . .



広告: LogRocket - Web アプリのための DVR

https://logrocket.com/signup/

LogRocket はフロントエンドツールです。
ご自身のブラウザで発生しているかのように、問題を再現することができます。
なぜエラーが発生したのかを推測したり、あるいはスクリーンショットやログのダンプをユーザーに依頼する代わりに、LogRocket はセッションを再現することで速やかに何が悪かったのかを理解できるように致します。
フレームワークによらずどのアプリでも完璧に動作します。
さらに Redux / Vuex / @ngrx/store からのコンテキストを追加でログに取るためのプラグインもあります。

Redux のアクションや状態をログに取ることに加えて、LogRocket はコンソールログ / JavaScript エラー / スタックトレース / ヘッダおよびボディ情報を含むネットワークのリクエストとレスポンス情報 / ブラウザのメタデータ / カスタムのログも記録します。
たとえ最も複雑なシングルページアプリケーションであったとしても、DOM の解析を行って HTML と CSS を記録することで、1px の狂いも無い映像で再現します。

無料でお試し頂けます。


お読み頂きどうもありがとうございました。

本記事は、以下の記事の翻訳です:
WebAssembly: How and why by Milica Mihajlija on LogRocket

To Milica: Thank you so much for your kind permission for me to translate your post.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .