[解決済み] Bluebirdのutil.toFastProperties関数は、どのようにしてオブジェクトのプロパティを「高速化」するのでしょうか?
質問
ブルーバードの
util.js
ファイル
は、次のような機能を持っています。
function toFastProperties(obj) {
/*jshint -W027*/
function f() {}
f.prototype = obj;
ASSERT("%HasFastProperties", true, obj);
return f;
eval(obj);
}
なぜか、return関数の後に文があるのです。
また、この件に関するJSHintの警告を作者が黙殺していたため、意図的なものと思われます。
return' の後の 'eval' が到達不能です。(W027)
この関数は具体的に何をするのですか?また
util.toFastProperties
は本当にオブジェクトのプロパティを高速化するのでしょうか?
BluebirdのGitHubリポジトリで、ソースコードにコメントがないか、課題リストに説明がないか探したのですが、見つかりませんでした。
解決方法は?
2017年更新です。まず、今日来る読者のために - Node 7 (4+)で動作するバージョンを紹介します。
function enforceFastProperties(o) {
function Sub() {}
Sub.prototype = o;
var receiver = new Sub(); // create an instance
function ic() { return typeof receiver.foo; } // perform access
ic();
ic();
return o;
eval("o" + o); // ensure no dead code elimination
}
1つか2つの小さな最適化を除けば、以下はすべて有効です。
まず、何をするのか、なぜその方が速いのか、そしてなぜそれが機能するのかを説明しましょう。
何をするのか
V8エンジンは、2つのオブジェクト表現を使用します。
- 辞書モード - として、オブジェクトがキーと値のマップとして格納されます。 ハッシュマップ .
- 高速モード - のようにオブジェクトが格納されます。 構造体 この場合、プロパティへのアクセスには計算が含まれない。
以下は
簡単なデモ
スピードの違いを実感してください。ここでは
delete
ステートメントを使用して、オブジェクトを強制的に低速ディクショナリモードにします。
エンジンは可能な限り高速モードを使用しようとします。一般的には、多くのプロパティアクセスが実行されるときは常に高速モードを使用しますが、時には辞書モードに投げ込まれることがあります。ディクショナリーモードになると、大きなパフォーマンス・ペナルティを受けるので、一般的にはオブジェクトを高速モードにすることが望ましいです。
このハックは、オブジェクトを辞書モードから強制的に高速モードにすることを目的としています。
- ブルーバードのペトカ本人 こちらで語っています .
- これらのスライド Vyacheslav Egorov氏も言及しています。
- この質問とその回答 も関連しています。
- この少し古い記事 は、v8でオブジェクトがどのように保存されるかについて、良いアイデアを与えてくれる読み物です。
高速化の理由
JavaScriptのプロトタイプは、通常、多くのインスタンスで共有される関数を格納し、動的に大きく変化することはほとんどありません。このため、関数が呼び出されるたびに余分なペナルティが発生しないように、高速モードで使用することが非常に望ましいのです。
このために - v8 は喜んで、オブジェクトが
.prototype
なぜなら、その関数をコンストラクタとして呼び出すことで生成されるすべてのオブジェクトで共有されるからです。これは一般的に賢い、望ましい最適化です。
仕組み
まず、コードを見て、それぞれの行が何を行っているかを把握しましょう。
function toFastProperties(obj) {
/*jshint -W027*/ // suppress the "unreachable code" error
function f() {} // declare a new function
f.prototype = obj; // assign obj as its prototype to trigger the optimization
// assert the optimization passes to prevent the code from breaking in the
// future in case this optimization breaks:
ASSERT("%HasFastProperties", true, obj); // requires the "native syntax" flag
return f; // return it
eval(obj); // prevent the function from being optimized through dead code
// elimination or further optimizations. This code is never
// reached but even using eval in unreachable code causes v8
// to not optimize functions.
}
私たちは 持つ は、v8がこの最適化を行っていると断言するために、自分自身でコードを見つける必要があります。 v8のユニットテストを読む :
// Adding this many properties makes it slow.
assertFalse(%HasFastProperties(proto));
DoProtoMagic(proto, set__proto__);
// Making it a prototype makes it fast again.
assertTrue(%HasFastProperties(proto));
このテストを読み、実行することで、この最適化がv8で確かに機能することがわかります。しかし - どのようにかを見るのは良いことでしょう。
を確認すると
objects.cc
は、以下のような関数を見つけることができます(L9925)。
void JSObject::OptimizeAsPrototype(Handle<JSObject> object) {
if (object->IsGlobalObject()) return;
// Make sure prototypes are fast objects and their maps have the bit set
// so they remain fast.
if (!object->HasFastProperties()) {
MigrateSlowToFast(object, 0);
}
}
今すぐ
JSObject::MigrateSlowToFast
は、明示的にDictionaryを受け取り、高速なV8オブジェクトに変換しているだけです。この本は読み応えがあり、V8オブジェクトの内部を知る上で興味深いものですが、ここでの主題ではありません。私は今でも
をご覧ください。
V8オブジェクトについて学ぶには良い方法だと思います。
をチェックアウトすると
SetPrototype
で
objects.cc
で、12231行目で呼び出されていることがわかります。
if (value->IsJSObject()) {
JSObject::OptimizeAsPrototype(Handle<JSObject>::cast(value));
}
で呼び出され、その後に
FuntionSetPrototype
で得られるものです。
.prototype =
.
行うこと
__proto__ =
または
.setPrototypeOf
でもよかったのですが、これらはES6の関数で、BluebirdはNetscape 7以降のすべてのブラウザで動作するので、ここでコードを簡略化するのは論外です。 例えば
.setPrototypeOf
を見ることができます。
// ES6 section 19.1.2.19.
function ObjectSetPrototypeOf(obj, proto) {
CHECK_OBJECT_COERCIBLE(obj, "Object.setPrototypeOf");
if (proto !== null && !IS_SPEC_OBJECT(proto)) {
throw MakeTypeError("proto_object_or_null", [proto]);
}
if (IS_SPEC_OBJECT(obj)) {
%SetPrototype(obj, proto); // MAKE IT FAST
}
return obj;
}
どれが直接
Object
:
InstallFunctions($Object, DONT_ENUM, $Array(
...
"setPrototypeOf", ObjectSetPrototypeOf,
...
));
というわけで、ペトカが書いたコードからベアメタルまでの道のりを歩んできたわけです。これはいいことです。
免責事項
これはすべて実装の詳細であることを忘れないでください。ペトカのような人は最適化フリークです。早まった最適化は97%の確率で諸悪の根源であることを常に念頭に置いてください。Bluebirdは非常に基本的なことを非常に頻繁に行うので、これらのパフォーマンスハックから多くの利益を得ます - コールバックと同じくらい速くなるのは簡単ではありません。あなたは めったに このようなことは、ライブラリに頼らないコードで行う必要があります。
関連
-
[解決済み] テスト
-
[解決済み】SyntaxError: JSONの位置1に予期しないトークンoがある。
-
[解決済み】React Nativeアプリをターミナルから実行するとエラーが発生する(iOS)
-
モジュールのビルドに失敗しました。Error: ENOENT: no such file or directory, scandir 'D:\.... \node_modules
-
[解決済み] 2つのJavaScriptオブジェクトのプロパティを動的にマージするにはどうすればよいですか?
-
[解決済み] AngularJSでデータバインディングはどのように機能するのですか?
-
[解決済み] JavaScriptでオブジェクトのキー/プロパティの数を効率的にカウントする方法
-
[解決済み] Facebookがブラウザに統合されたDeveloper Toolsを無効にする方法を教えてください。
-
[解決済み] JavaScriptのオブジェクトのクラスを取得する方法は?
-
[解決済み】関数の前のエクスクラメーションマークは何をするのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Vueの要素ツリーコントロールに破線を追加する説明
-
[解決済み】Uncaught SyntaxError: JSONの位置0に予期しないトークンuがあります。
-
[解決済み】ローカルファイルを開くことができません - Chrome: ローカルリソースの読み込みが許可されていない
-
[解決済み】JavaScriptの配列でforEachが関数でない不具合
-
[解決済み】GETできない / Nodejsエラー
-
[解決済み】"フォームが接続されていないため、フォームの送信がキャンセルされました "というエラーの取得について
-
[解決済み】Node.js Error: Cannot find module express
-
[解決済み] 期待される代入または関数呼び出し: 未使用式なし ReactJS
-
nullのプロパティinnerHTMLを読み取れません エラーメッセージ
-
OSSアップロードエラーを解決する: net::ERR_SSL_PROTOCOL_ERROR