1. V8ソースV8という名前は、この車の「V型8気筒エンジン」(V8エンジン)に由来しています。 V8エンジンは主にアメリカで開発され、その高馬力で広く知られています。 V8 エンジンは、強力で高速な JavaScript エンジンであることをユーザーに示すために Google が名付けました。 V8 が誕生する前、初期の主流の JavaScript エンジンは JavaScriptCore エンジンでした。 JavaScriptCore は主に、Apple によって開発されオープンソース化された Webkit ブラウザ カーネルを提供します。 GoogleはJavaScriptCoreとWebkitの開発速度と実行速度に満足しなかったため、新しいJavaScriptエンジンとブラウザカーネルエンジンの開発を開始し、現在最も人気のあるブラウザ関連ソフトウェアとなったV8とChromiumという2大エンジンが誕生したと言われています。 2. V8サービスターゲットV8 は Chrome をベースに開発されましたが、ブラウザ カーネルに限定されません。 V8 はこれまで、人気の Node.js、weex、クイック アプリケーション、初期の RN など、多くのシナリオに適用されてきました。 3. V8の初期アーキテクチャV8 エンジンは、速度とメモリのリサイクルに革命を起こすという使命を持って誕生しました。 JavaScriptCore のアーキテクチャは、バイトコードを生成し、バイトコードを実行することです。 Google は、JavaScriptCore アーキテクチャは実現可能ではなく、バイトコードを生成すると時間がかかり、マシンコードを直接生成するほど高速ではないと考えています。そのため、V8 は初期のアーキテクチャ設計において非常に革新的であり、マシンコードに直接コンパイルする方法を採用しました。その後の実践で、Google のアーキテクチャは確かに速度を向上させましたが、メモリ消費の問題も引き起こすことが判明しました。 V8 の初期のフローチャートを見てみましょう。 初期の V8 には、Full-Codegen と Crankshaft という 2 つのコンパイラがありました。 V8 はまず、Full-Codegen を使用してすべてのコードを 1 回コンパイルし、対応するマシン コードを生成します。 JS の実行中に、V8 の組み込みプロファイラーはホットな関数を選択し、パラメータのフィードバック タイプを記録し、最適化のために Crankshaft に渡します。したがって、Full-Codegen は基本的に最適化されていないマシン コードを生成しますが、Crankshaft は最適化されたマシン コードを生成します。 IV. V8の初期アーキテクチャの欠陥バージョンが導入され、Web ページがより複雑になるにつれて、V8 のアーキテクチャ上の欠陥が徐々に明らかになりました。
5. V8の現在のアーキテクチャ上記の欠点を解決するために、V8 は JavaScriptCore アーキテクチャを採用してバイトコードを生成します。 Google はここで一周回ったように感じますか? V8 はバイトコードを生成する方法を使用します。全体的なプロセスは次のとおりです。 Ignition は V8 のインタープリターであり、その本来の目的はモバイル デバイスのメモリ消費を削減することでした。 Ignition 以前は、V8 のフルコード生成ベースライン コンパイラによって生成されたコードが、通常、Chrome の JavaScript ヒープ全体の 3 分の 1 近くを占めていました。これにより、Web アプリケーションの実際のデータ用のスペースが少なくなります。 Ignition のバイトコードは TurboFan で直接使用でき、Crankshaft のようにソース コードから再コンパイルすることなく、最適化されたマシン コードを生成できます。 Ignition のバイトコードは、V8 でよりクリーンでエラーの少ないベースライン実行モデルを提供し、V8 の適応型最適化の重要な機能であるデオプティマイゼーション メカニズムを簡素化します。最後に、バイトコードの生成は Full-codegen のベースライン コンパイル済みコードの生成よりも高速であるため、Ignition を有効にすると、スクリプトの起動時間が改善され、Web ページの読み込み時間も短縮されます。 TurboFan は V8 の最適化コンパイラです。TurboFan プロジェクトはもともと、Crankshaft の欠点を解決するために 2013 年後半に開始されました。 Crankshaft は JavaScript 言語のサブセットのみを最適化できます。たとえば、構造化例外処理、つまり JavaScript の try、catch、finally キーワードで区切られたコード ブロックを使用して JavaScript コードを最適化するようには設計されていません。 Crankshaft で新しい言語機能のサポートを追加するのは困難です。これらの機能では、サポートされている 9 つのプラットフォームに対してアーキテクチャ固有のコードを記述することがほぼ必須だからです。 新しいアーキテクチャを採用する利点 異なるアーキテクチャにおける V8 のメモリ比較を図に示します。 結論: Ignition+TurboFan アーキテクチャのメモリ使用量は、Full-codegen+Crankshaft アーキテクチャと比較して半分以上削減されていることがわかります。 異なるアーキテクチャにおける Web ページ速度の改善の比較を図に示します。 結論: Ignition+TurboFan アーキテクチャでは、Full-codegen+Crankshaft アーキテクチャと比較して、Web ページの速度が 70% 向上することが明確にわかります。 次に、既存のアーキテクチャの各プロセスについて簡単に説明します。 6. V8の語彙解析と構文解析コンパイラ理論を学んだことのある学生なら、JS ファイルは単なるソースコードであり、マシンでは実行できないことを知っています。字句解析とは、ソースコードの文字列を分割し、一連のトークンを生成することです。下の図に示すように、異なる文字列は異なるトークンの種類に対応します。 語彙分析の次の段階は文法分析です。文法解析の入力は字句解析の出力であり、出力は AST 抽象構文木です。プログラムで構文エラーが発生すると、V8 は構文解析フェーズで例外をスローします。 7. V8 AST 抽象構文木次の図はadd関数の抽象構文木データ構造である。 V8 解析ステージの後、次のステップは抽象構文木に基づいてバイトコードを生成することです。次の図に示すように、add 関数は対応するバイトコードを生成します。 BytecodeGenerator クラスの機能は、抽象構文木に従って対応するバイトコードを生成することです。異なるノードはバイトコード生成関数に対応し、関数は Visit**** で始まります。 + 記号に対応する関数バイトコードは以下のように生成されます。 void バイトコードジェネレータ::VisitArithmeticExpression(バイナリ演算* expr) { フィードバックスロットスロット = feedback_spec()->AddBinaryOpICSlot(); 式* サブ式; Smi* リテラル; if (expr->IsSmiLiteralOperation(&subexpr, &literal)) { VisitForAccumulatorValue(サブ式); ビルダー()->SetExpressionPosition(expr); ビルダー()->BinaryOperationSmiLiteral(expr->op(), リテラル, feedback_index(スロット)); } それ以外 { レジスタ lhs = VisitForRegisterValue(expr->left()); アクセプタンス値の訪問(expr->right()); builder()->SetExpressionPosition(expr); // デバッグ用にソースコードの位置を保存します builder()->BinaryOperation(expr->op(), lhs, feedback_index(slot)); // 追加バイトコードを生成します } } 上記から、ソース コードの場所レコードがあることがわかります。次の図は、ソース コードとバイトコードの場所の対応を示しています。 バイトコードを生成し、そのバイトコードをどのように実行するのでしょうか?次に説明しましょう: 8. バイトコードまず、V8 バイトコードについて説明します。 各バイトコードは、入力と出力をレジスタオペランドとして指定します。 点火はレジスタr0、r1、r2...とアキュムレータレジスタを使用する レジスタ: 関数パラメータとローカル変数は、ユーザーに見えるレジスタに格納されます。 アキュムレータ: 中間結果を格納するために使用される、ユーザーには見えないレジスタ ADD バイトコードを以下に示します。 バイトコード実行 次の一連の図は、各バイトコードが実行されたときの対応するレジスタとアキュムレータの変化を示しています。add 関数はパラメータ 10 と 20 を渡し、アキュムレータによって返される最終結果は 50 です。 各バイトコードは処理関数に対応しており、バイトコード ハンドラーのアドレスは dispatch_table_ に保存されます。バイトコードが実行されると、対応するバイトコード ハンドラーが呼び出されて実行されます。 Interpreter クラスのメンバー dispatch_table_ は、各バイトコードのハンドラー アドレスを格納します。 たとえば、ADD バイトコードに対応する処理関数は次のとおりです (ADD バイトコードが実行されると、InterpreterBinaryOpAssembler クラスが呼び出されます)。 IGNITION_HANDLER(追加、インタープリターバイナリオペレーションアセンブラー) { BinaryOp のフィードバック付き(&BinaryOpAssembler::Generate_AddWithFeedback); } void BinaryOpWithFeedback(BinaryOpGenerator ジェネレータ) { ノード* reg_index = BytecodeOperandReg(0); ノード* lhs = LoadRegister(reg_index); ノード* rhs = GetAccumulator(); ノード*コンテキスト = GetContext(); ノード* slot_index = BytecodeOperandIdx(1); ノード* feedback_vector = LoadFeedbackVector(); バイナリ演算アセンブラ binop_asm(state()); ノード*結果 = (binop_asm.*generator)(コンテキスト、lhs、rhs、slot_index、 フィードバックベクトル、false); SetAccumulator(result); // ADD計算の結果をアキュムレータに設定する Dispatch(); // 次のバイトコードを処理する } 実際、この時点で JS コードが実行されています。実行プロセス中にホット関数が見つかった場合、V8 は Turbofan を有効にして最適化されたコンパイルを行い、マシンコードを直接生成します。以下では、Turbofan 最適化コンパイラについて説明します。 9. ターボファンTurbofan は、バイトコードとホット関数のフィードバック タイプに基づいて最適化されたマシン コードを生成します。Turbofan の最適化プロセスの多くは、基本的にコンパイル原則のバックエンド最適化と同じであり、sea-of-node を使用します。 関数の最適化を追加します: 関数 add(x, y) { x+y を返します。 } 追加(1, 2); %OptimizeFunctionOnNextCall(追加); 追加(1, 2); V8 には、どの関数を最適化するかを指定するために直接呼び出すことができる関数があります。%OptimizeFunctionOnNextCall を実行して、Turbofan を積極的に呼び出し、add 関数を最適化します。add 関数は、前回の呼び出しのパラメータ フィードバックに基づいて最適化されます。当然、今回のフィードバックは整数なので、turbofan はパラメータが整数であることに基づいて最適化し、直接マシン コードを生成します。次の関数呼び出しでは、最適化されたマシン コードを直接呼び出します。 (V8 を実行するには --allow-natives-syntax が必要であることに注意してください。OptimizeFunctionOnNextCall は組み込み関数です。--allow-natives-syntax を使用した場合にのみ、JS は組み込み関数を呼び出すことができ、それ以外の場合は実行時にエラーが報告されます)。 JS の add 関数によって生成される対応するマシン コードは次のとおりです。 これには小さな整数の概念が関係します。こちらの記事をご覧ください https://zhuanlan.zhihu.com/p/82854566 追加関数の入力パラメータを文字に変更すると 関数 add(x, y) { x+y を返します。 } 追加(1, 2); %OptimizeFunctionOnNextCall(追加); 追加(1, 2); 最適化された add 関数によって生成される対応するマシン コードは次のとおりです。 上記の 2 つの図を比較すると、add 関数は異なるパラメータを渡し、最適化後に異なるマシン コードを生成します。 整数が渡された場合、基本的にはaddアセンブリ命令を直接呼び出します。 文字列が渡された場合、基本的にはV8の組み込みAdd関数が呼び出されます。 この時点で、V8 の全体的な実行プロセスは終了します。 以上がJavaScriptエンジンV8の実行プロセスの詳細な説明です。JavaScriptエンジンV8の詳細については、123WORDPRESS.COMの他の関連記事に注目してください。 以下もご興味があるかもしれません:
|
<<: ソケット '/tmp/mysql.sock' 経由でローカル MySQL に接続できない解決策
>>: Docker基盤技術の適用に関する詳細な説明 名前空間Cgroup
AWS - Amazon のクラウド コンピューティング サービス プラットフォーム以前、AWS の...
戦争パッケージを準備する1. 既存のSpringBootプロジェクトを準備し、pomに依存関係を追加...
多くの友人がフォーラムやメッセージエリアで、どのような状況で MySQL をシャーディングする必要が...
重要なデータはバックアップする必要があり、リアルタイムでバックアップする必要があります。そうしないと...
序文gdb は Linux で非常に便利なデバッグ ツールです。コマンドライン モードのデバッグ ツ...
<input> はユーザー情報を収集するために使用され、終了ステートメントはありません。...
1. 問題Linux 上の mysql5.7 のパスワードを忘れました2. 解決策• ステップ 1:...
直接コード: タイプとして「bigint unsigned」、バイトとして「8」、max_numとし...
目次序文1. Props、$emit一方向データフロー2. $親、$子3. $attrs、$list...
この記事の冒頭で、以前書いた入門記事の間違いを訂正したいと思います。初心者を再び誤解させないように、...
この記事では、例を使用して MySQL プロセス関数の一般的な使用方法を説明します。ご参考までに、詳...
コーディングをしていると、多くのことが同じ結末を迎えることに気づくでしょう。問題を解決する方法は何千...
最近、vue について読みました。これまで基本的に見落としていた単一ファイル コンポーネントを見つけ...
Ubuntuでsshを開くのに1時間以上かかりました。主な原因は、最初に読んだチュートリアルの手順...
誰についてシステムにログインしているユーザーを表示します。 who コマンドを実行すると、現在システ...