Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
JavaScriptエンジンから見るランタイム / 2024-04-25
Search
Sho Miyamoto
April 25, 2024
12
1.8k
JavaScriptエンジンから見るランタイム / 2024-04-25
Sho Miyamoto
April 25, 2024
Tweet
Share
More Decks by Sho Miyamoto
See All by Sho Miyamoto
Proxy-Status & Cache-Status
shqld
0
510
High Performance JavaScript / jsconfjp2019
shqld
0
500
FastlyとTypeScriptで実現するカナリアリリース / yamagoya2020
shqld
19
7k
ServiceWorkerの開発 / ServiceWorker Development
shqld
2
640
Loading Performanceとの向き合い方 / InsideFrontend 2019
shqld
9
2.1k
日経電子版とPWAのこれから / PWANight vol.2
shqld
2
4.7k
日経電子版のマイクロフロントエンドとPWA / devsum2019
shqld
8
12k
Featured
See All Featured
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
131
33k
Typedesign – Prime Four
hannesfritz
40
2.4k
Happy Clients
brianwarren
98
6.7k
Designing the Hi-DPI Web
ddemaree
280
34k
How to Ace a Technical Interview
jacobian
276
23k
Automating Front-end Workflow
addyosmani
1366
200k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5k
5 minutes of I Can Smell Your CMS
philhawksworth
202
19k
Side Projects
sachag
452
42k
The Cult of Friendly URLs
andyhume
78
6k
How GitHub (no longer) Works
holman
310
140k
Designing Experiences People Love
moore
138
23k
Transcript
JavaScriptエンジンから見る ランタイム @shqld (2024/04/25)
自己紹介 - あざらし - @shqld - 勉強会の参加はコロナ禍前以来なので 緊張しています - 上野でお酒を飲みながらJSの話をしている
あざらしがいたらそれはおそらく自分です
- 近年、ランタイム(サーバサイド)が増えている - Cloudflare Workers (workerd), Bun, LLRT, WinterJS, …
- これまでは採用されるエンジンV8が支配的だった(Node, Deno) - 実行パフォーマンス、セキュリティ、 API、... - 目的に照らして最適なエンジンを選択する時代に(?) - 起動時間、メモリフットプリント、ポータビリティ、カスタム性、 ... - エンジンの観点からランタイムを見てみる 👀 はじめに・まとめ
- 話すこと - エンジンの割と基本的な話 - エンジンとは, どういう構造なのか, etc. - 各エンジン・ランタイムの特徴
- 話さないこと - それぞれのトピックの詳細 - 処理系の最適化手法など - (発表時間が10mなので各話題 1 ページずつで軽く触れていきます) ⚠
- エンジン - 言語処理系 - e.g. V8, … - ランタイム
- ホスト環境、実行環境 - e.g. Chrome(blink), Node.js, … - 処理系を動かす環境 - JSと外界を仲介しているとも言える?( e.g. DOM, fs, net, …) - 例えばconsoleも非同期処理も持っていないエンジンがある エンジンとランタイム
- 基本的にはランタイムがエンジンを埋め込む形 - e.g. V8 の C++ API をラップして Rust
プログラムから呼ぶ - ランタイム兼エンジンもある( e.g. Hermes) - とはいえエンジン単体で動く CLI も大体ある(主にデバッグ・テスト用) - jsvu でまとめて簡単にインストールできる - ランタイム側でのカスタマイズ - host-definedな言語機能を実装する - e.g. イベントループ(非同期処理) - グローバルなAPIを生やす - e.g. Buffer, setTimeout, … エンジンとランタイムの関係
この発表で触れるエンジン / ランタイム - V8 - JavaScriptCore - SpiderMonkey -
QuickJS - Hermes - Wasmtime - (Engine262) - Node.js - Deno - workerd - Bun - WinterJS - LLRT - Hermes
JSエンジンの外観
エンジンの構成 - JSを実行するための様々な機能を持つプログラム - インタープリタだったり JITコンパイラだったり - ブラウザに搭載されるエンジンは巨大 - JITコンパイラやインタプリタを複数持っている
- 実行フェーズごとに使い分けている - ブラウザ非搭載の後発エンジンは比較的軽量なことが多い - 主にバイトコードインタプリタ( VM)のみの構成が多い - (そもそも新規でtest262をパスするエンジンを作るのは大変) - (V8レベルのエンジンを作るのはもっと大変、そもそも目的が違うのでモチベーションも少ない はず)
ブラウザ搭載エンジン - ウェブの進化に伴い、ブラウザ(レンダリングエンジン)に求められるものが増え た - 必然的にJSエンジンへの要求も多くなる - パフォーマンス - e.g.
JIT機構 - セキュリティ - e.g. Sandbox機構, バグバウンティの実施
パフォーマンス: JIT コンパイル - tl;dr: 実行中にバイトコードの一部をネイティブコードにコンパイルして部分的に置き換えて実行 する - 大変 -
アーキテクチャごとに対応が必要 - コンパイルできるコードとできないコードがある - プロファイリング - 最適化すべき処理を決めるための情報(実行回数 , サイズ, メモリ, …) - 型や引数などの情報 - 脱最適化 - 想定と異なるコードに遭遇したら最適化前のコードに戻す - 段階 (Execution Tier) がある - Baseline JIT, Optimizing JIT, etc.
パフォーマンス: Execution Tier - プロファイリング結果によって実行段階が上がっていく - Baseline - Optimizing -
例 - V8 - Ignition (int.) -> SparkPlug (baseline) -> Maglev (opt.) -> TurboFan (opt.2) - JavaScriptCore - LLInt (interpreter) -> Baseline (baseline) -> DFG (opt.) -> FTL (opt.2) -
https://cabulous.medium.com/how-v8-javascript-engine-works-5393832d80a7 *黒い枠線は筆者( @shqld)
一般的なエンジンの構造 (JITがある場合) - 内部処理系 (Execution Tier) が複数ある - Baseline Interpreter
-> Bytecode Interpreter -> JIT Compiler - それに伴い内部表現も複数ある - AST -> Bytecode -> IR (1 … n) -> Native Code - プログラムの実行フェーズの移行に伴い処理系・表現も移行
- 多くのエンジンは1つのプロセスから複数の実行インスタンスを起動できる - ブラウザにおいては、例えばページ・ frame・workerごとにインスタンスが分かれている - ページごとにJS実行のためにエンジンのプロセスを立ち上げていたら遅い - 実行コンテキストを隔離する必要がある -
もし仮に隔離されていなければ、悪意のあるサイトが iframe経由で親frameのコンテキストを盗 み見できたりコードを実行できたりする - * ブラウザのレンダリングプロセスの話も関わるので一概には言えないが ... - (そもそもES仕様に実行コンテキスト間の通信は規定されていない) - Realmのような同一実行コンテキスト内の話とは別 セキュリティ: Sandbox
JSエンジンとランタイムの紹介
V8 ランタイム - Chromium (blink) - Node.js - Deno -
workerd - …
V8 - ブラウザ搭載 - 超高速 - 複数のExecution Tierを持つ - JITを持つ
- (有名なので今更あまり書くことがない) - カスタムスナップショットAPI - (wasmtime + wizer のくだりで必要なので話したかったが、時間の関係上割愛) - Node.js も対応している (experimental)
Node.js - 非同期処理のバックエンドはlibuv - (割愛)
Deno - 非同期処理のバックエンドはTokio - (割愛)
workerd - Cloudflare Workers で使われるランタイム - * 正確にはコアの部分はworkerdと共通だが違うコードベースとのこと - JS
on Edge の先駆け・代表格 - サンドボックス環境 - エッジでは、独立した大量のアプリケーションを高速に実行する必要がある - 従来の(VM、)コンテナの代わりに V8 Isolate を利用している - ServiceWorker API - 通常のサーバサイドランタイムとは異なり、 ブラウザのServiceWorker APIに近いAPIを 実装している - req/resやCacheの操作など、エッジとSWで 共通する箇所が多く理にかなっている https://blog.cloudflare.com/introducing-cloudflare-workers/
- 実行インスタンス - 1つのコンテキストを持つ - コンテキスト: 実行に必要なデータ - e.g. グローバル、ランタイムのカスタム
API、スナップショット - 高速かつ軽量 - コンテナと比べて起動時間やメモリ使用量 が大幅に小さい - CF Workersの各アプリケーションは ブラウザでいうページ、 frame (内のJS部分)に当たる V8 Isolate https://developers.cloudflare.com/workers/reference/how-workers-works/
JavaScriptCore ランタイム - Safari (WebKit) - Bun (new!) - …
JavaScriptCore - ブラウザ搭載 - (割愛)
- NodeやDenoと同じく一般的なサーバサイドランタイム - JavaScriptCoreを利用している理由は不明 ... - Bunが速い理由にエンジンが JSCであることは関係ないらしい - 単にランタイムのコードであらゆる工夫・最適化をしているだけとのこと
- Zig 製 (🦭ランタイム部分でのトピックは色々あるが、エンジンとの関連でいうとあまり思いつかなかった) Bun
ランタイム - FireFox (Gecko) - WinterJS (new!) - … SpiderMonkey
SpiderMonkey - C++, Rust 製 - ブラウザ搭載 - MDNによるとBrendan Eich氏によって作られた世界初の
JSエンジン - https://developer.mozilla.org/en-US/docs/Web/JavaScript/JavaScript_technologies_overview#javascript_implementations - Wasmにコンパイルできる - が、JITはあまり効かない - 代わりに Portable Baseline Interpreter が色々最適化
WinterJS - WinterCG 準拠 - Wasm にコンパイルできる - Rust で書かれている
& SpiderMonkey を利用している - 非同期バックエンドは Tokio - 開発元であるWasmerでは WinterJS をWasmにコンパイルしてから実行される 🤯
ランタイム - LLRT (new!) - … QuickJS
QuickJS - C 製 - 軽量 - JIT なし・BytecodeInterpreterのみ -
参照カウントベースのGC - 起動時間が高速・メモリ使用量が少ない - シンプルで使いやすいAPI - これからQuickJSを利用するエンジンが増えていくのでは - Wasmにコンパイルできる - QuickJS とアプリケーションコードをまとめて Wasm ランタイム上でJSを実行するプロジェクトもある (e.g. javy) - node:vmの代わりに Wasm にコンパイルした QuickJS を読み込んでアプリケーションで起動・実行する例 もある
* ./sample.js は空文字列
- Rust 製 - AWS が開発、主に Lambda での実行を想定 - 起動時間やメモリ使用量を高速化する狙い
- デプロイされている既存コード (Node.js) との互換性もある - 起動が高速 - 一番の要因はJITを持たず軽量なQuickJSを採用したこと? - CPU-intensiveな処理や長時間動くアプリケーション以外では、 JIT が活きることは少なさそう - 🦭: サンドボックスといったセキュリティ面の担保はどうなってるのか気になる LLRT (Low Latency Runtime)
Hermes ランタイム - ReactNative
- ReactNative 向けに作られたエンジン(すごい) - AOT コンパイラ + バイトコードインタプリタ - 最適化済みのバイトコードを事前に出力する
- ブラウザとは異なり、RNは実行する対象コードが事前に与えられている - SSAまで落とし込んで解析・最適化する( V8の Maglev レベル?) - JITなし - 事前に最適化済みバイトコードがあるためそこまでモチベーションがなさそう - JITがあるとiOSで使えない(e.g. セキュリティ、消費電力) - experimentalで開発されていたが、現在は開発がストップしている(っぽい) - Hermes Static という AOT Compiler も開発中 - コンパイル対象はバイトコードではなくネイティブコード - その他のランタイムとは異なり実行マシンも決まっている Hermes
Static Hermes - 絶賛開発中でまだ使われていないが、今後 Hermes に導入されるっぽい? - https://github.com/facebook/hermes/tree/static_h - エンジンではなくTS/Flowの静的コンパイラ
- AssemblyScript に近い存在 - ネイティブコードを出力するので C/C++ 並のパフォーマンスとなる - コード生成は LLVM - shermes = LLVM フロントエンド - 🦭 個人的に昨年からずっと気になっているプロジェクト - 時間が足りないので詳しくは開発者の発表を見てください https://speakerdeck.com/tmikov2023/static-hermes-react-native-eu-2023-announcement
おまけ: Engine262 - 新しい仕様の試験実装などのために開発されているエンジン - 言語はJS - 仕様の記述に忠実に実装されていて分かりやすい - 仕様は抽象的な記述が多いので、実装コードとの対応関係が分かりづらいことが多い
- Completion Record や Execution Contexts といった仕様上の概念 (?)も忠実に実装されている - Abstract Operations はそれぞれ一つの関数として実装されている
- WebAssemblyランタイムの一つ - WASIをサポートしている - wasmtimeを利用してJSを実行するプラットフォームが存在する - e.g. Shopify function,
Fastly Compute@Edge, … - JSを実行するといっても内部ではJSエンジンが実行されている - 1. JSエンジンをプログラムとして( Wasmにコンパイルして).wasmファイルに埋め込む - 2. JSソースコードをデータとして .wasmファイルに埋め込む - 3. JSソースコードを引数に JSエンジンを実行するWasmプログラムが完成する おまけ: wasmtime
おまけ: wasmtime + wizer - JS on Wasm で使われている最適化 -
JSエンジンのスナップショットと同じ方式
おまけ: WASI + JS Runtime = V8 Isolate ?
- 近年、ランタイム(サーバサイド)が増えている - Cloudflare Workers, Bun, LLRT, WinterJS, … -
これまでは採用されるエンジンV8が支配的だった(Node, Deno) - 実行パフォーマンス、セキュリティ、 API、... - 目的に照らして最適なエンジンを選択する時代に(?) - 起動時間、メモリフットプリント、ポータビリティ、カスタム性、 ... - エンジンの観点からランタイムを見てみる 👀 はじめに・まとめ