5

コードの最適化は SO でここで述べられており、プロファイリングは JavaScript を最適化するための最初のステップであり、推奨されるエンジンは Chrome と Firefox のプロファイラーです。それらの問題は、各関数が実行された時間を奇妙な方法で伝えることですが、私はそれらをよく理解していません。最も役立つ方法は、各行が実行された回数と、可能であれば各行に費やされた時間もプロファイラーが通知することです。このようにして、ボトルネックを厳密に確認することができます。しかし、そのようなツールが実装/発見される前に、2 つのオプションがあります。

1)特定のコードブロックまたは行が実行された時間と回数の両方をカウントする独自の計算機を作成する 2)遅いメソッドとそうでないメソッドを理解することを学ぶ

オプション 2については、 jsperf.comが非常に役立ちます。配列の最適化について学び、 JSPERF.COMで速度テストを行いました。次の画像は、5 つの主要なブラウザーでの結果を示しており、以前は知らなかったいくつかのボトルネックが見つかりました。

スピードテスト

主な調査結果は次のとおりです。

1) 配列への値の代入は、代入に使用される方法にかかわらず、通常の変数への代入よりも大幅に遅くなります。

2) パフォーマンスが重要なループの前に配列を事前に初期化および/または事前充填すると、速度が大幅に向上します。

3) 数学三角関数は、値を配列にプッシュするのに比べてそれほど遅くはありません(!)

各テストの説明は次のとおりです。


1. 非配列 (100%):

変数には、次のように定義済みの値が与えられました。

var non_array_0=0;
var non_array_1=0;
var non_array_2=0;
...

時限地域では、次のように呼ばれていました。

non_array_0=0;
non_array_1=1;
non_array_2=2;
non_array_3=3;
non_array_4=4;
non_array_5=5;
non_array_6=6;
non_array_7=7;
non_array_8=8;
non_array_9=9;

上記は配列のような変数ですが、配列とは反対に、他の方法でこれらの変数を反復または参照する方法はないようです。それともありますか?

このテストでは、数値を変数に代入するよりも高速なものはありません。


2.non_array_non_pre (83.78%)

テスト 1 とまったく同じですが、変数は事前に初期化も事前入力もされていません。速度は、テスト 1 の速度の 83,78% です。テストしたすべてのブラウザーで、事前に入力された変数の速度は、事前に入力されていない変数よりも高速でした。そのため、速度が重要なループの外側で変数を初期化 (および場合によっては事前入力) します。

テストコードは次のとおりです。

var non_array_non_pre_0=0;
var non_array_non_pre_1=0;
var non_array_non_pre_2=0;
var non_array_non_pre_3=0;
var non_array_non_pre_4=0;
var non_array_non_pre_5=0;
var non_array_non_pre_6=0;
var non_array_non_pre_7=0;
var non_array_non_pre_8=0;
var non_array_non_pre_9=0;

3. pre_filled_array (19.96 %):

配列は悪です!通常の変数 (test1 と test2) を捨てて、配列を全体像に取り込むと、速度が大幅に低下します。すべての最適化 (配列の事前初期化と事前入力) を行い、ループやプッシュを行わずに値を直接割り当てますが、速度は 19.96% に低下します。これは非常に悲しいことであり、なぜこのようなことが起こるのか本当に理解できません。これは、このテストで私が受けた主なショックの 1 つです。配列は非常に重要であり、私は配列なしで多くのものを作る方法を見つけていません。

テストデータは次のとおりです。

pre_filled_array[0]=0;
pre_filled_array[1]=1;
pre_filled_array[2]=2;
pre_filled_array[3]=3;
pre_filled_array[4]=4;
pre_filled_array[5]=5;
pre_filled_array[6]=6;
pre_filled_array[7]=7;
pre_filled_array[8]=8;
pre_filled_array[9]=9;

4. non_pre_filled_array (8.34%):

これは 3 と同じテストですが、配列メンバーは事前に初期化も事前入力もされておらず、最適化のみが事前に配列を初期化していました。var non_pre_filled_array=[];

速度は、事前に初期化されたテスト 3 と比較して 58,23 % 低下します。したがって、配列を事前に初期化および/または事前に入力すると、速度が 2 倍になります。

テストコードは次のとおりです。

non_pre_filled_array[0]=0;
non_pre_filled_array[1]=1;
non_pre_filled_array[2]=2;
non_pre_filled_array[3]=3;
non_pre_filled_array[4]=4;
non_pre_filled_array[5]=5;
non_pre_filled_array[6]=6;
non_pre_filled_array[7]=7;
non_pre_filled_array[8]=8;
non_pre_filled_array[9]=9;

5. pre_filled_array[i] (7.10%):

それからループへ。このテストで最速のループ方法。配列は事前に初期化され、事前に入力されています。

インライン バージョン (テスト 3) と比較した速度低下は 64,44 % です。これは非常に顕著な違いであるため、必要がなければループしないでください。配列のサイズが小さい場合 (どのくらい小さいかはわかりません。個別にテストする必要があります)、ループの代わりにインライン代入を使用する方が賢明です。

また、速度の低下が非常に大きく、実際にループが必要なため、より適切なループ方法(例: ) を見つけることが賢明while(i--)です。

テストコードは次のとおりです。

for(var i=0;i<10;i++)
{
  pre_filled_array[i]=i;
}

6. non_pre_filled_array[i] (5.26%):

配列を事前に初期化して事前に入力しない場合、速度は 25.96% 低下します。繰り返しますが、速度が重要なループの前に事前初期化および/または事前充填することは賢明です。

コードは次のとおりです。

for(var i=0;i<10;i++) 
{
  non_pre_filled_array[i]=i;
}

7. 数学計算 (1.17%):

すべてのテストは何らかの基準点でなければなりません。数学関数は遅いと見なされます。テストは 10 の「重い」数学計算で構成されていましたが、このテストで私を驚かせたもう 1 つのことがあります。ループ内で 10 個の整数を配列にプッシュする 8 と 9 の速度を見てください。これらの 10 個の数学関数の計算は、ループ内で 10 個の整数を配列にプッシュするよりも 30% 以上高速です。したがって、いくつかの配列プッシュを事前に初期化された非配列に変換し、それらの三角法を維持する方が簡単かもしれません。もちろん、フレームごとに数百または数千の計算がある場合は、たとえば次のように使用するのが賢明です。sin/cos/tan の代わりに sqrt を使用し、距離の比較にはタクシーの距離を使用し、角度の比較にはダイヤモンド角度 (t-ラジアン) を使用します。、それでも主なボトルネックは別の場所にある可能性があります。ループはインライン化より遅く、プッシュは事前初期化や事前入力による直接代入を使用するより遅く、コード ロジック、描画アルゴリズム、および DOM アクセスは遅くなる可能性があります。すべてを Javascript で最適化することはできません (画面に何かを表示する必要があります!)。ここ SO の誰かが、コードは人間のためのものであり、メンテナンス コストが最大のコストであるため、高速なコードよりも読みやすいコードが重要であると述べています。これは経済的な観点ですが、コードの最適化により、優雅さと可読性とパフォーマンスの両方を実現できることがわかりました。そして、5% のパフォーマンス向上が達成され、コードがより単純になると、良い感触が得られます!

コードは次のとおりです。

non_array_0=Math.sqrt(10435.4557);
non_array_1=Math.atan2(12345,24869);
non_array_2=Math.sin(35.345262356547);
non_array_3=Math.cos(232.43575432);
non_array_4=Math.tan(325);
non_array_5=Math.asin(3459.35498534536);
non_array_6=Math.acos(3452.35);
non_array_7=Math.atan(34.346);
non_array_8=Math.pow(234,222);
non_array_9=9374.34524/342734.255;

8. pre_filled_array.push(i) (0.8%):

プッシュは悪です!プッシュ合体ループは凶悪!これは、何らかの理由で値を配列に代入するための非常に遅い方法です。テスト 5 (ループでの直接代入) は、この方法よりもほぼ 9 倍高速であり、どちらの方法もまったく同じことを行います。つまり、整数 0 ~ 9 を事前に初期化され事前に入力された配列に割り当てます。このプッシュ フォー ループの悪さが、プッシュまたはループ、あるいは両方の組み合わせ、またはループ カウントによるものかどうかはテストしていません。JSPERF.COM には、競合する結果をもたらす他の例があります。実際のデータだけでテストして判断する方が賢明です。このテストは、使用されたもの以外のデータと互換性がない場合があります。

コードは次のとおりです。

for(var i=0;i<10;i++)
{
  pre_filled_array.push(i);
}

9. non_pre_filled_array.push(i) (0.74%):

このテストの最後で最も遅い方法はテスト 8 と同じですが、配列は事前に入力されていません。9 より少し遅いですが、差はそれほど大きくありません (7.23%)。しかし、例を挙げて、この最も遅い方法と最も速い方法を比較してみましょう。この方法の速度は方法 1 の速度の 0.74% であり、これは方法 1 がこれよりも 135 倍高速であることを意味します。したがって、特定のユースケースで配列が必要な場合は、慎重に検討してください。1回または数回のプッシュのみの場合、合計速度の違いは目立ちませんが、一方でプッシュが少ない場合は、非配列変数に変換するのが非常に簡単でエレガントです。

これはコードです:

for(var i=0;i<10;i++)
{
  non_pre_filled_array.push(i);
}

最後に必須の SO の質問:

このテストによると、非配列変数代入と配列代入の速度差が非常に大きいように見えるため、非配列変数代入の速度と配列のダイナミクスを取得する方法はありますか?

var variable_$i = 1$i が整数に変換されるようにループで使用することはできません。テストで証明されたvar variable[i] = 1よりも大幅に遅いものを使用する必要があります。var variable1 = 1これは、大規模な配列がある場合にのみ重要になる可能性があり、多くの場合は重要です。


編集:配列アクセスの遅さを確認するために新しいテストを作成し、より高速な方法を見つけようとしました:

http://jsperf.com/read-write-array-vs-variable

配列の読み取りおよび/または配列の書き込みは、通常の変数を使用する場合よりも大幅に遅くなります。配列メンバーに対していくつかの操作が行われる場合は、配列メンバーの値を一時変数に格納し、それらの操作を一時変数に対して行い、最後に値を配列メンバーに格納する方が賢明です。また、コードは大きくなりますが、これらの操作はループ内よりもインラインで行う方がはるかに高速です。

結論:配列と通常の変数は、ディスクとメモリに似ています。通常、メモリ アクセスはディスク アクセスよりも高速であり、通常の変数アクセスは配列アクセスよりも高速です。また、連結操作も中間変数を使用するよりも高速である可能性がありますが、これによりコードが少し読みにくくなります。


4

1 に答える 1