11

だから、私は2DJavascript物理シミュレーションをプログラミングしています。パフォーマンスは良好ですが、改善のために最適化を行っています。したがって、プログラムは多くの物理ジオメトリで動作するため、プログラムでいくつかのピタゴラス定理の計算を行います。全部で約5つの計算。合わせて、1秒間に約100万回実行されます。そのため、その単純なピタゴラス定理コードを新しい関数に入れて呼び出すと、パフォーマンスが向上すると考えました。結局のところ、そうすれば、ブラウザーはコンパイルを行う必要が少なくなります。そこで、Firefoxでコードを実行すると、その計算の実行時間が4000000%増加しました。

どのように?これは同じコードです:Math.sqrt(x * x + y * y)では、関数として追加するとどのように遅くなりますか?その理由は、コードを実行せずに関数を呼び出すだけで時間がかかり、1秒あたりのこれらの遅延が100万回追加されると、関数の速度が低下するためだと思います。

それは私にはかなり憂慮すべきことのようです。これは、事前定義されたjs関数にも適用されますか?それはありそうもないようです、もしそうなら、彼らはどうやってそれを避けますか?

以前のコードは次のようになりました。

function x()
{
    dx=nx-mx;
    dy=ny-my;
    d=Math.sqrt(dx*dx+dy*dy);
    doStuff(...
}

私が試したのはこれでした:

function x()
{
    dx=nx-mx;
    dy=ny-my;
    d=hypo(dx,dy);
    doStuff(...
}
function hypo(x,y)
{
    return Math.sqrt(x*x+y*y);
}

ありがとう!

4

2 に答える 2

8

関数呼び出しはごくわずかであるか、JSがこれまでになかったコンパイル済み言語で最適化することさえできます。それを超えて、ブラウザに大きく依存します。

それらは、JSが主にごく最近まで行ってきた通訳言語でのすべてのパフォーマンスの死です。最近のほとんどのブラウザーには、過去のJSインタープリターからの大幅なアップグレードであるJIT(Just In Time)コンパイラーがありますが、JSの呼び出しオブジェクトは実際に呼び出されているものを判別する必要があるため、別のスコープへの関数呼び出しにはまだオーバーヘッドがかかると思います。さまざまなスコープチェーンを上下に移動します。

したがって、原則として、IE8以前のバージョンのChromeとFirefoxを気にする場合は、関数呼び出し期間を避けてください。特にループ内。JITブラウザーの場合、他の関数内で定義された関数が一般的に有益であると期待します(ただし、これはIE9のまったく新しいテクノロジーであり、他のすべての人にとっては比較的新しいため、テストします)。

注意すべきもう1つのこと。関数が特に複雑な場合、JITはそれらを最適化するために何もしない可能性があります。

https://groups.google.com/forum/#!msg/closure-compiler-discuss/4B4IcUJ4SUA/OqYWpSklTE4J

ただし、理解しておくべき重要なことは、関数内の関数のように、何かがロックされてコンテキスト内でのみ呼び出される場合、JITは簡単に最適化できるはずです。関数の外部で定義され、その関数のどの定義が正確に呼び出されているかを判別する必要があります。それは外部機能にある可能性があります。グローバルである可能性があります。これは、ウィンドウオブジェクトのコンストラクターのプロトタイプなどのプロパティである可能性があります。関数がファーストクラスである言語では、データを渡すのと同じ方法で参照を引数として渡すことができるため、そのステップを実際に回避することはできません。あなたの現在の文脈の外。

したがって、X内でhypoを定義して、何が起こるかを確認してください。

JITでまだ価値があるかもしれない、解釈された時代からのもう2つの一般的なヒント:

  • '。' のような演算子someObject.propertyは、キャッシュする価値のあるプロセスです。使用するたびに関連する呼び出しオブジェクト検索プロセスがあるため、オーバーヘッドが発生します。親オブジェクトまたはプロトタイプを変更すると、特定のコンテキスト外で実際に参照するものが変更される可能性があるため、Chromeはこのプロセスの結果を保持しないと思います。あなたの例では、xがループによって使用されている場合(xがJITのループと同じ関数で定義されている場合は大丈夫または役立つ可能性があります-インタープリターでの殺人)、使用する前にMath.sqrtを変数に割り当ててみますハイポで。現在の関数のコンテキスト外のものへの参照が多すぎると、一部のJITは、最適化するのに苦労する価値がないと判断する可能性がありますが、それは私の側の純粋な推測です。

  • 配列をループさせる最も速い方法は、おそらく次のとおりです。

//assume a giant array called someArray
var i = someArray.length; //note the property lookup process being cached here
//'someArray.reverse()' if original order isimportant
while(i--){
  //now do stuff with someArray[i];
}

注:コードブロックが何らかの理由でここで機能していません。

このようにすると、基本的にインクリメント/デクリメントステップと論理比較がデクリメントだけに変換され、左右の比較演算子が完全に不要になるため、便利です。JSでは、右側のデクリメント演算子は、iが渡されて評価され、ブロック内で使用される前にデクリメントされることを意味することに注意してください。while(0)falseと評価されます。

于 2012-04-13T00:19:44.530 に答える
1

驚いたことに、Erikが提案したようにルックアップをキャッシュしても、ブラウザー(Chromium、Linux)のパフォーマンスはそれほど向上しませんが、代わりにパフォーマンスが低下するようです:http: //jsperf.com/inline-metric-distance

var optimizedDistance = (function () { 
    var sqrt = Math.sqrt;
    return function (x, y) { return sqrt(x * x + y * y); }
})();

より遅い_

var unoptimizedDistance = function(x, y) {
    return Math.sqrt(x * x + y * y);
}

エイリアスの呼び出しでさえ遅い

var _sqrt = Math.sqrt; // _sqrt is slower than Math.sqrt!

しかし、繰り返しになりますが、これは正確な科学ではなく、実際の測定値は依然として変化する可能性があります。

それにもかかわらず、私はを使用して行きMath.sqrtます。

于 2012-04-13T08:44:18.333 に答える