2017-06-09 4 views
4

JavaScriptでは、オブジェクトの形状を変更しないようにすることが一般的に推奨されています。これは私が思ってしまうV8/spidermonkey/chakraの内部の知識があれば、JavaScriptで未定義のオブジェクトメンバーを明示的に初期化することは最適化ですか?

は、この

class Foo { 
    constructor() { 
    } 

    baz(x) { this.bar = x; } 
} 

、これはどのように真または偽であるよりも高いパフォーマンスが得られます。この

class Foo { 
    constructor() { 
    this.bar = undefined; 
    } 

    baz(x) { this.bar = x; } 
} 

やりがいのあるベストプラクティスですか?どうして? 1つのJSエンジンで他のエンジンよりも多かれ少なかれ真実ですか?

+1

一方で、それを測定してみることはできますが、最初のバージョンがどうにかして2番目のバージョンよりも優れていると思う理由は明確ではありません。 – pvg

+0

なぜあなたは最初のバージョンが候補者だと思いますか? –

+1

はい、一般的な最適化ですが、実際に違いを生むかどうかは、インスタンスで行う操作の種類によって大きく異なります。 – Bergi

答えて

9

V8デベロッパーはこちら。

はい、最初のバージョンは一般的には価値あるベストプラクティスです。

その理由は、ではなく、というオブジェクトの作成自体が高速になるためです。逆に、作業をしないコンストラクタは、何らかの作業を行うコンストラクタよりも少なくとも少し速くなることは明らかです。

アプリケーションのすべてのFooオブジェクトが同じ「シェイプ」を持つことを保証するため、最初のバージョンが推奨される理由は、2番目のバージョンでは、.barプロパティとその他のもの't。プロパティは時々存在し、時にはJavaScriptエンジンを使用可能な最速の状態/コードパスから離れる傾向がありません。そのような特性が複数ある場合、効果はより大きくなる。例として

class Foo() { 
    constructor() {} 
    addBar(x) { this.bar = x; } 
    addBaz(x) { this.baz = x; } 
    addQux(x) { this.qux = x; } 
} 
var foo1 = new Foo(); foo1.addBar(1); 
var foo2 = new Foo(); foo2.addBaz(10); foo2.addBar(2); 
var foo3 = new Foo(); foo3.addQux(100); foo3.addBaz(20); foo3.addBar(3); 

function hot_function(foo) { 
    return foo.bar; // [1] 
} 
hot_function(foo1); 
hot_function(foo2); 
hot_function(foo3); 

ラインでは、コンストラクタのこのバージョンでは、[1]とマークは、少なくとも3つの異なる形状のオブジェクトが見られます。したがって、JavaScriptエンジンはオブジェクト内の少なくとも3つの異なる場所にプロパティbarを見つけます。内部実装の詳細によっては、毎回すべてのオブジェクトのプロパティを検索する必要があるかもしれません。あるいは、以前に見たオブジェクトシェイプをキャッシュできるかもしれませんが、いくつかのキャッシュはキャッシュするよりも高価です。 。 コンストラクタがundefinedにすべてのプロパティを初期化していた場合、ここに入ってくるfooオブジェクトはすべて同じ形状になり、barプロパティは常に最初のプロパティになり、エンジンはこの非常に単純なケースを処理するために。

ちょうどそのようなロードではなく、addBar()は既存のプロパティを上書きするか(非常に高速)、新しいプロパティを追加する必要がありますか(割り当てが必要な場合があります)オブジェクトをコピーする)、または両方のケース間で動的に決定する必要があります(もちろん、最も遅い)。

もう1つの効果は、一意のオブジェクトのそれぞれの形状がある量の内部メタデータを必要とすることです。不必要に異なるオブジェクト形状を避けることで、メモリを節約できます。

もちろん、このような小さな例では、効果は小さくなります。しかし、それぞれ数十のプロパティを持つ何千ものオブジェクトを持つ大きなアプリがあれば、それは本当に大きな違いを生むことができます。誤ったマイクロベンチマークに注意してください!

+1

これは型の変更が実質的なパフォーマンスヒットになる可能性があるため、コンストラクタで 'undefined'に初期化することはできません。少なくともこの質問(Chrome 61-ish)では、遅い初期化か、コンストラクタ内でセッターに渡されるのと同じ型の値に初期化するよりもずっと遅いです。 – pvg

+2

型の変更によって発生するさまざまな種類のパフォーマンスヒットを避けることは、コンストラクタのフィールドを初期化することが推奨される理由です。はい、後の型がわかっている(そして常に同じである)場合、同じ型の値に初期化するとビット数が増える(特に数値の場合)が、 'undefined'に初期化すると完全にうまくいっています利益のそして、私が言ったように、コンストラクターが空のときはもちろん、初期化だけでも高速です。後続のプログラムの実行には、すべてのフィールドが初期化されているために利点があります。 – jmrk

+0

私がうんざりしているのは、合理的なプログラミング練習と合理的によく知られているv8のパフォーマンス練習であるコンストラクタの初期化ではない(私は、2012年にそれを取り上げたパフォーマンスに関するGoogle IOの話があったと思う)。しかし、すべてを「未定義」に設定することは、プログラミングの難しさとパフォーマンスの両方の練習のようです。 – pvg

関連する問題