防弾JavaScriptテスト

JS速度テストの作成は、思ったほど簡単ではありません。 ブラウザ間の互換性の問題に触れなくても、多くの落とし穴に陥ることがあります。



それが私がjsPerfをした理由です。 誰もがテストを作成して共有し、さまざまなコードのパフォーマンスをチェックできるようにするシンプルなWebインターフェイス。 何も心配する必要はありません。パフォーマンスを測定する必要があるコードを入力するだけで、jsPerfが新しいテストタスクを作成し、それをさまざまなデバイスやさまざまなブラウザで実行できます。



舞台裏では、jsPerfは最初にJSLitmusライブラリ(私はBenchmark.jsと呼びます)を使用しました 。 時間が経つにつれて、彼女は新しい機会を獲得し、最近、 ジョン・デイビッド・ダルトンはすべてをゼロから書き直しました。



この記事では、JSテストの開発時に発生する可能性のあるさまざまなトリッキーな状況に光を当てます。



テストパターン



JSコードのテストを実行してパフォーマンスをテストする方法はいくつかあります。 最も一般的なオプション、 テンプレートA



var totalTime, start = new Date, iterations = 6; while (iterations--) { //     } // totalTime →  ,      totalTime = new Date - start;
      
      







テストコードは、指定された回数実行されるループに配置されます(6)。 その後、開始日が終了日から差し引かれます。 このようなテンプレートは、 SlickSpeedTaskspeedSunSpider、およびKrakenテストフレームワークで使用されます。



問題


デバイスとブラウザのパフォーマンスが常に向上しているため、一定回数の繰り返しを使用するテストでは、作業の結果として0 msが増加しますが、これは必要ありません。



パターンB


2番目のアプローチは、一定時間内に完了する操作の数を計算することです。 さらに、反復回数を選択する必要はありません。



 var hz, period, startTime = new Date, runs = 0; do { //     runs++; totalTime = new Date - startTime; } while (totalTime < 1000); //  ms   totalTime /= 1000; // period →      period = totalTime / runs; // hz →     hz = 1 / period; //     // hz = (runs * 1000) / totalTime;
      
      







コードを約1秒間実行します。 totalTimeが1000ミリ秒を超えるまで。



テンプレートBは、 DromaeoおよびV8ベンチマークスイートで使用されます。



問題


ガベージコレクション、エンジンの最適化、およびその他のバックグラウンドプロセスにより、同じコードの実行時間が変わる場合があります。 したがって、テストを何度も実行し、結果を平均化することをお勧めします。 V8 Suiteはテストを1回だけ実行します。 Dromaeo-5回、しかしこれは時々十分ではありません。 たとえば、テストの最小実行時間を1000ミリ秒から50ミリ秒に減らして、繰り返し実行するための時間を増やします。



パターンC


JSLitmusは2つのテンプレートを組み合わせます。 テンプレートAを使用してループでテストをn回実行しますが、テストの最小実行時間が入力されるまで、実行時にループが適応してnを増やします。 パターンBのように。



問題


JSLitmusはテンプレートAの問題を回避しますが、テンプレートBの問題から逃れません。 キャリブレーションでは、テストの3つの最速の繰り返しが選択され、他の結果から減算されます。 残念ながら、「3つのベスト」は統計的に最良の方法ではありません。 テストを何度も実行し、平均結果からキャリブレーション平均を差し引いても、結果の誤差が大きくなるとキャリブレーション全体が消費されます。



パターンD


以前のパターンの問題は、関数のコンパイルとループの展開によって解消できます。



 function test() { x == y; } while (iterations--) { test(); } // …  → var hz, startTime = new Date; x == y; x == y; x == y; x == y; x == y; // … hz = (runs * 1000) / (new Date - startTime);
      
      







問題


しかし、欠点もあります。 関数をコンパイルすると、メモリ使用量が増加し、作業が遅くなります。 テストを数百万回繰り返すと、非常に長い行を作成し、巨大な関数をコンパイルします。



ループの展開に関する別の問題は、テストが作業の開始時にリターンを介して出口を編成できることです。 関数が3行目に戻った場合、100万行をコンパイルしても意味がありません。 このような場合、これらのポイントを追跡し、テンプレートAを使用する必要があります。



身体機能の抽出


Benchmark.jsは異なるテクノロジーを使用しています。 これらすべてのパターンの最良の側面が含まれていると言えます。 メモリを節約するためにサイクルを展開しません。 精度に影響する要因を減らし、テストがローカルのメソッドと変数で機能するように、各テストの関数本体を抽出します。 例:



 var x = 1, y = '1'; function test() { x == y; } while (iterations--) { test(); } // …  → var x = 1, y = '1'; while (iterations--) { x == y; }
      
      







その後、抽出されたコードをwhileループ(パターンA)で実行し、指定された時間が経過するまで繰り返し(パターンB)、統計的に有意な結果を得るためにすべてを何度も繰り返します。



あなたが注意を払う必要があるもの



正しくないタイマー動作


OSとブラウザの組み合わせによっては、 さまざまな 理由でタイマーが正しく機能しない場合があります。 たとえば、Windows XPを読み込むとき、割り込み時間は通常10〜15ミリ秒です。 つまり、OSは10ミリ秒ごとにシステムタイマーから割り込みを受け取ります。 一部の古いブラウザ(IE、Firefox 2)は、OSタイマーに依存しています(たとえば、Date()の呼び出し)。GetTime()は、OSから直接データを受信します。 また、タイマーが10〜15ミリ秒ごとにのみ更新されると、測定の不正確さが蓄積されます。



ただし、これは回避できます。 JSでは、 最小時間単位を取得できます。 その後、テスト実行時間を計算して、エラーが1%を超えないようにする必要があります。 エラーを取得するには、この最小単位を半分に分割する必要があります。 たとえば、Windows XPではIE6を使用し、最小単位は15ミリ秒です。 エラーは15ミリ秒/ 2 = 7.5ミリ秒です。 このエラーが測定時間の1%を超えないようにするには、エラーを0.01で除算します:7.5 / 0.01 = 750 ms。



その他のタイマー


--enable-benchmarkingフラグパラメーターを指定して起動すると、ChromeとChromiumはchrome.Intervalメソッドへのアクセスを提供します。これにより、マイクロ秒までの高解像度タイマーを使用できます。 Benchmark.jsで作業しているとき、John-David DaltonはJavaナノ秒タイマーに出会い、 小さなjava-appletを介してJSからそれにアクセスしました。



高解像度タイマーを使用すると、テスト時間を短く設定できるため、結果としてエラーが少なくなります。



FirebugはFirefoxでJITを無効にします


起動されたFirebugアドオンはジャストインタイムコンパイルを無効にするため、すべてのテストはインタープリターで実行されます。 彼らは通常よりもずっとゆっくりと動作します。 テストの前に必ずFirebugを無効にしてください。



それほどではありませんが、同じことがWeb InspectorとOperaのDragonflyにも当てはまります。 結果に影響を与えないように、テストを実行する前にそれらを閉じてください。



機能とブラウザのバグ


ループを使用するテストは、さまざまなブラウザーのバグの影響を受けます。IE9では、 デッドコードを削除する機能を備えた例が示されていますMozilla TraceMonkeyエンジンのバグ、またはOpera 11のquerySelectorAll結果のキャッシュにより 、正しい結果が得られない場合があります。 それらを覚えておく必要があります。



統計的有意性


John Rezig記事では、ほとんどのテストで統計的に有意な結果が得られない理由が説明されています。 要するに、各結果の誤差の大きさを常に評価し、あらゆる可能な方法でそれを減らす必要があります。



クロスブラウザテスト


ブラウザーの実際の異なるバージョンでスクリプトをテストします。 たとえば、IEの互換モードに依存しないでください。 また、IEは8番目のバージョンまでスクリプトを500万命令に制限していました。 システムが高速であれば、スクリプトはそれらを0.5秒で実行できます。 この場合、ブラウザに「スクリプト警告」メッセージが表示されます。 次に、レジストリで許可されている操作の数を編集する必要があります。 または、この制限を修正するプログラムを使用してください。 幸いなことに、IE9では既に削除されています



おわりに



いくつかのテストを実行している場合でも、テストスイートを作成している場合でも、ライブラリを作成している場合でも、JSテストの質問には多くの隠れた問題があります。 Benchmark.jsとjsPerfは毎週更新され、バグを修正し、新しい機能を追加して、テストの精度を高めています。



All Articles