JSの仕組み:WebAssemblyの機能と範囲

本日は、JavaScriptに関連するすべての作業の機能に特化した一連の資料の第6部を紹介します。 ここでは、WebAssemblyについて説明します。 つまり、この技術を詳細に分析し、その機能と、パフォーマンスの観点から通常のJavaScriptとどのように関連するかを検討します。 コードの読み込み時間、プログラムの実行速度、ガベージコレクション、メモリ使用量、プラットフォームAPIへのアクセス、デバッグ、マルチスレッド、WebAssemblyコードの移植性について説明します。 この技術は、現在その開発のごく初期段階にありますが、すでにWebアプリケーションの開発に関する見解を変え始めています。 開発者がブラウザーコードで最高のパフォーマンスを必要とする場合、WebAssemblyについて知る必要があります。



画像






[アドバイスを読む]サイクルの他の19の部分
パート1: エンジン、ランタイムメカニズム、コールスタックの概要

パート2: V8内部とコードの最適化について

パート3: メモリ管理、4種類のメモリリーク、およびそれらとの戦い

パート4: イベントループ、非同期、および非同期/待機を使用してコードを改善する5つの方法

パート5: WebSocketとHTTP / 2 + SSE。 何を選ぶ?

パート6: WebAssemblyの機能と範囲

パート7: Web Workersと5つの使用シナリオ

パート8: サービスワーカー

パート9: Webプッシュ通知

パート10: MutationObserverを使用してDOMの変更を追跡する

パート11: Webページレンダリングエンジンとパフォーマンスを最適化するためのヒント

パート12: パフォーマンスとセキュリティを最適化するブラウザーネットワークサブシステム

パート12: パフォーマンスとセキュリティを最適化するブラウザーネットワークサブシステム

パート13: CSSとJavaScriptを使用したアニメーション

パート14: JSの仕組み:抽象構文ツリー、解析、およびその最適化

パート15: JSの仕組み:クラスと継承、BabelおよびTypeScriptでのトランスピレーション

パート16: JSの仕組み:ストレージ

パート17: JSの仕組み:Shadow DOMテクノロジーとWebコンポーネント

パート18: JSの仕組み:WebRTCおよびP2P通信メカニズム

パート19: JSの仕組み:カスタム要素


WebAssemblyとは何ですか?



WebAssembly(「wasm」と略される)は、Webアプリケーション用の効率的な低レベルのバイトコードです。 Wasmは、JavaScript以外の言語(C、C ++、Rustなど)でWebページの機能を開発することを可能にします。 これらの言語のコードは、WebAssemblyで(静的に)コンパイルされます。 その結果、迅速にロードされ、非常に高いパフォーマンスを持つWebアプリケーションができます。



読み込み時間



JavaScriptプログラムを実行するために、ブラウザは最初に保存され、プレーンテキストでネットワーク経由で送信されるすべての.jsファイルをダウンロードする必要があります。



Wasmは、アセンブラーに似た低レベル言語です。 インターネット経由で非常にコンパクトなバイナリ形式で既にコンパイルされたファイルを転送する必要があるため、WebAssemblyプログラムはブラウザでより高速にロードされます。



フルフィルメント



今日、wasmプログラムはマシンコードよりも20%遅いだけです。 これは間違いなく価値のある結果です。 結局のところ、特別な環境でコンパイルされ、高レベルのセキュリティを提供する多くの制限を使用して起動される形式について話しているのです。 この世界のマシンコードと比較したそのようなスローダウンはそれほど大きく見えません。 さらに、将来、wasmコードのパフォーマンスが期待されています。 さらに興味深いのは、wasmがプラットフォームに依存しないことです。 そのサポートは、すべての主要なブラウザエンジンで利用でき、wasmコードの実行時にほぼ同じパフォーマンスを示します。



wasmとJavaScriptの機能を比較するには、 この資料を参照しください。 この資料では、V8の例を使用してJSエンジンの機能について説明しています。 ここで、wasmコードが他のV8エンジンとどのように関連するかについて説明します。



WasmおよびJ8エンジンV8



V8エンジンのデバイスの図、つまり、JavaScriptプログラムが単純なテキストファイルから実行可能なコードに移動するパスを示します。









V8での動的コンパイル



左側には、特定の機能を含むJavaScriptのソースコードがあります。 最初に、このコードが解析され、文字列がトークンに変換され、 抽象構文ツリー (AST)が生成されます。 ASTは、JSプログラムのロジックの表現です。 AST V8は作成されると、発生したことをマシンコードに変換します。 抽象構文ツリーがクロールされ、テキストの形式で存在していた関数であったものがコンパイルされたバージョンに変換されます。 同時に、V8はコードを最適化するために多くの努力をしません。



次に、エンジンの次の段階で何が起こるかを見てみましょう。









V8エンジンコンベア



ここでは、最適化V8コンパイラーの1つであるTurboFanの時期が来ていることを示しています。 V8でのJavaScriptアプリケーションの操作中に、多くの補助アクションが実行されます。 つまり、TurboFanは、対応するコードのセクションを最適化するために、ボトルネックと最も一般的に使用されるプログラムフラグメントの検索で何が起こっているかを監視します。 その後、TurboFanはプログラムの最も重要な部分を処理し、それらを最適化JITコンパイラーに渡します。これにより、プロセッサー時間のほとんどを「食い尽くす」機能がはるかに高速になります。



このアプローチにより、JavaScriptのパフォーマンスを向上させ、プログラムの速度の問題を解決することができますが、ここではすべてがスムーズではありません。 実際には、最適化が必要なものと不要なものに関するコード分析と意思決定操作もリソースを消費します。 これは、システムの消費電力が高くなることを意味し、モバイルデバイスの場合は、1回の充電で寿命が短くなります。



上記のスキームにwasmを含めた場合、このコードは分析といくつかのコンパイルパスを必要としないことがわかります。 すでに最適化されており、すぐに使用できます。









WasmおよびコンベヤーV8



Wasmコードは、静的コンパイル中に最適化されます。 それを使用する場合、テキストファイルを解析する必要はありません。 wasmのおかげで、マシンコードにしか変換できないバイナリファイルを自由に使用できます。 このコードに対するすべての改善は、コンパイル中に行われ、ブラウザに入る前に行われます。

プログラムテキストを最適化されたマシンコードに変換する多くの手順をスキップできるため、これによりwasmの実行がより効率的になります。



メモリモデル









信頼できるWebAssemblyメモリ領域と信頼できないWebAssemblyメモリ領域



たとえば、C ++で記述され、WebAssemblyでコンパイルされたプログラムのメモリは、「穴」のない連続したブロックです。 セキュリティの向上に役立つwasmの機能の1つは、実行スタックを線形アドレス空間から分離することです。 C ++-プログラムにはたくさんあり、スタックのメモリはその上部に割り当てられます。 メモリを操作するこの編成では、ポインターを使用してスタックメモリにアクセスし、変数の状態に影響を与えることができます。変数の相互作用は、プログラム実行の現在の段階では提供されません。 多くの悪意のあるプログラムが使用するのはこの機会です。



WebAssemblyは完全に異なるメモリモデルを使用します。 実行スタックはwasmプログラム自体が格納されているメモリから分離されているため、このメモリへの不正アクセスを取得したり、一部の変数の状態を変更したりする方法はありません。 さらに、関数はポインターを使用せず、整数オフセットを使用します。 ここでは間接アドレス指定メカニズムが使用されます。 必要な直接アドレスは、プログラムのプロセスで計算されます。 このメカニズムは、複数のwasmモジュールを同時にロードできるように構築されており、アドレスはオフセットを使用して特定され、最終的にはすべてが正常に機能します。



JavaScriptメモリ管理メカニズムの詳細については、 こちらをご覧ください



ガベージコレクション



このシリーズの以前の資料では、ガベージコレクター(GC)がJSプログラムのメモリの管理に関与していると既に述べました。



WebAssemblyの場合、すべてが少し異なって見えます。 このテクノロジーは、手動メモリ言語でサポートされています。 その結果、独自のガベージコレクターをwasmモジュールと共に使用できますが、これは簡単な作業ではありません。



WebAssemblyは、C ++およびRustで使用されるメモリメソッドに焦点を合わせています。 wasmは低レベルのテクノロジーであるため、アセンブラーの1つ上にあるプログラミング言語を容易にwasmにコンパイルできるのは論理的です。 そのため、Cでプログラミングする場合は通常のmalloc



コマンドを使用でき、C ++ではスマートポインターを使用できます。 Rustは完全に異なるアプローチを取ります(すべてが完全に異なるため、これには入らないでください)。 これらの言語は、ガベージコレクターを使用しないため、メモリ管理を担当する複雑なランタイムメカニズムは必要ありません。 WebAssemblyは、同様のメモリモデルに完全に適合します。



さらに、これらの言語は、DOMの操作など、通常JavaScriptを使用して実装される複雑な操作を実行するようには設計されていません。 C ++はそのようなアプリケーション用に設計されていないため、HTMLアプリケーション全体をC ++で記述することは意味がありません。 ほとんどの場合、C ++またはRustのWebアプリケーションのコードは、WebGLで動作するように、または数学計算を担当するライブラリなど、高度に最適化されたライブラリを作成するように記述されています。



ただし、将来的には、他のメモリモデルを使用する言語のサポートが期待されています。



外部APIへのアクセス



ランタイムに応じて、JavaScriptプログラムは特殊なAPIと直接対話する場合があります。 たとえば、プログラムがブラウザ用に作成されている場合、ブラウザまたはデバイスを制御し、 DOMCSSOMWebGLIndexedDBWeb Audio APIなどを操作するためにアプリケーションが使用する一連のWeb API自由使用できます



WebAssemblyモジュールは、プラットフォームが提供するAPIに直接アクセスできません。 モジュールはJavaScriptを介してのみAPIと対話できます。 wasm-moduleから同様のAPIにアクセスする必要がある場合、JavaScriptを介してこの呼び出しを行う必要があります。 たとえば、 console.log



コマンドを実行する必要がある場合、JSを介して呼び出す必要があります。 このようなJavaScriptツールの呼び出しはパフォーマンスに影響します。



いつもそうだとは言えません。 将来的には、適切なAPIがwasmコードで直接使用されることが期待されています。 その結果、JavaScript呼び出しを使用せずにwasmアプリケーションを作成できました。



コードマップ



JSコードを縮小した後、デバッグする必要がある場合、 コードマップ(ソースマップ)が有効になります。 これは、さまざまなファイルから縮小または結合されたJSコードとその初期状態との対応を確立する方法です。 プロジェクトが実稼働用にアセンブルされると、ファイルの縮小と組み合わせが実行され、ソースファイルに関する情報を格納するコードマップが作成されます。 生成されたコード内の特定の場所を参照するときは、コードマップをチェックして、プログラムのパッケージバージョンよりもはるかに理解しやすいソースプログラムのフラグメントを見つけることができます。



対応する仕様がまだないため、WebAssemblyはソースマップをサポートしていませんが、そのような機会がすぐに現れる可能性は十分にあります。



その結果、たとえばC ++から取得したwasmコードをデバッグするときに、ソースコードを表示し、その中にブレークポイントを設定することが可能になります。 少なくとも、これはwasmにコードマップを埋め込むことの目標です。



マルチスレッド



JavaScriptは単一のスレッドで実行されますが、非同期プログラミングモデルをサポートしています。 詳細についてはこちらをご覧ください 。 さらに、JSはWeb Workersテクノロジーをサポートしていますが、かなり特定の範囲を持っています。



ほとんどの場合、プロセッサリソースを集中的に使用する計算は、ユーザーインターフェイスのメインスレッドをブロックする可能性があります。 ただし、Web WorkersコードはDOMを使用できません。



WebAssemblyは現在マルチスレッドをサポートしていません。 ただし、ほとんどの場合、この機会はすぐに現れます。 Wasmは低レベルのスレッド(つまり、C ++で使用されるスレッド)に近くなります。 「実際の」スレッドを操作する機能は、ブラウザーアプリケーションの開発に多くの新しい機会をもたらします。 しかし、もちろん、マルチスレッドは新たな困難の発生も意味します。



コードの移植性



JavaScriptアプリケーションは、ブラウザ、サーバー、組み込みシステムなど、ほぼすべての場所で機能します。



WebAssemblyは、セキュリティとコードの移植性を考慮して設計されています。 これにより、JavaScriptと非常によく似ています。 wasmをサポートするすべての環境、つまり、任意のブラウザーで動作します。



移植性の観点から、WebAssemblyには、Javaがアプレットを通じて達成しようとした目標と同じ目標があります。



まとめ



WebAssemblyの以前のバージョンでは、数学などの重いコンピューティングに特別な注意が払われていました。 このような計算のアプリケーションの明らかな領域は、無数のピクセルを操作しなければならないゲームです。 このようなアプリケーションは、OpenGLで作業する通常の方法を使用してC ++ / Rustで記述し、wasmで発生することをコンパイルできます。 その後、これはすべてブラウザで機能します。 例として、Firefoxでこのリンクを開きます。 Unreal Engineを使用します。



Webアプリケーションのパフォーマンスの向上を目的としたWebAssemblyを使用する別のオプションは、このテクノロジを使用してリソースを大量に消費する計算を実行するライブラリ、たとえば画像処理用のライブラリを実装することです。



wasmを使用すると、モバイルデバイスのバッテリーのエネルギー消費を大幅に削減できます(もちろん、これもエンジンに依存します)。実行のためにプログラムを準備するための補助操作のほとんどは、静的コードのコンパイル中に実行されます。



将来、コンパイルするコードを書かなくてもバイナリwasmファイルを使用できるようになると予想されます。 NPMでは、このアプローチを実装するプロジェクトを見つけることができます。



wasmがJSを置き換えると言うことは可能ですか? 開発のこの段階では、技術は間違いなく違います。 たとえば、DOMでの作業やコードが実行されるプラットフォームのAPIの使用について話している場合、これらのAPIは抽象レベルを追加せずにJSプログラムから直接アクセスできるため、これまでJavaScriptに代わるものはありません。



この記事の著者は、 SessionStackが WebAssemblyに関心を持って注視おり、このテクノロジーの開発で最もリソースを消費する部分を加速することを望んでいると述べています。



一連の記事の前の部分:



パート1: JSの仕組み:エンジン、ランタイムメカニズム、コールスタックの概要

パート2: JSの仕組み:V8内部とコードの最適化について

パート3: JSの仕組み:メモリ管理、4種類のメモリリーク、およびそれらとの戦い

パート4: JSの仕組み:イベントループ、非同期、およびasync / awaitを使用してコードを改善する5つの方法

パート5: JSの仕組み:WebSocketとHTTP / 2 + SSE。 何を選ぶ?



親愛なる読者! プロジェクトでWebAssemblyを使用する予定はありますか?



All Articles