web-dev-qa-db-ja.com

C#でパフォーマンスの最適化を単体テストするにはどうすればよいですか?

構築している検索コードで、レーベンシュタインのアルゴリズムの最適化されたバージョンを使用しています。アルゴリズムが正しい結果を返していることを確認するための機能ユニットテストがありますが、このコンテキストでは、アルゴリズムのパフォーマンスも非常に重要です。

将来の変更が最適化に影響を与える場合、それらが失敗したテストとして表示されるように、プロジェクトにテストカバレッジを追加しようとしています-アルゴリズムは決定論的であり、既知のテストデータに対して実行されているため、これはカウントと同じくらい詳細になる可能性があります特定のテスト入力のセットに対して実行された命令の数。言い換えれば、タイマーを使用してアルゴリズムのパフォーマンスを測定することは考えていません。出力だけでなく、アルゴリズムの内部動作を実際にテストすることに関心があります。

C#/。NET 4でこれにどのようにアプローチするかについてのアイデアはありますか?

編集:壁時計時刻だけを使用したくない理由は、CPU負荷や、テストの制御外の他の要因によって変化するためです。たとえば、ビルドサーバーに負荷がかかっている場合、テストが失敗する可能性があります。 will展開されたシステムの一部として実時間監視があります。

編集2:このように考えてください...パフォーマンスが重要な要件である場合、どのように赤->緑->リファクタリングを適用しますか?

25
Dylan Beattie

私はこれを何度か成功させたので、あなたの質問の3番目の部分に答えるつもりです。

パフォーマンスが重要な要件である場合、赤->緑->リファクタリングをどのように適用しますか?

  1. 変更する予定の内容や、変更の結果として速度が低下する可能性のあるその他の方法について、リグレッションをキャッチするためのピン留めテストを作成します。
  2. 失敗するパフォーマンステストを作成します。
  3. すべてのテストを頻繁に実行して、パフォーマンスを向上させます。
  4. 固定テストを更新して、パフォーマンスをより厳密に固定します。

ピン留めテストを書く

このようなヘルパーメソッドを作成して、ピン留めしたいものの時間を計ります。

private TimeSpan Time(Action toTime)
{
    var timer = Stopwatch.StartNew();
    toTime();
    timer.Stop();
    return timer.Elapsed;
}

次に、メソッドに時間がかからないことを確認するテストを作成します。

[Test]
public void FooPerformance_Pin()
{
    Assert.That(Time(()=>fooer.Foo()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(0));
}

失敗した場合(失敗メッセージに実際の経過時間が表示されている場合)、実際の時間より少し長い時間で時間を更新します。再実行すると合格します。変更によってパフォーマンスに影響を与える可能性のある他の関数についてもこれを繰り返し、最終的には次のようになります。

[Test]
public void FooPerformance_Pin()
{
    Assert.That(Time(()=>fooer.Foo()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(0.8));
}
[Test]
public void BarPerformance_Pin()
{
    Assert.That(Time(()=>fooer.Bar()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(6));
}

失敗したパフォーマンステストを書く

私はこの種のテストを「ベイティングテスト」と呼んでいます。これは、ピン留めテストの最初のステップにすぎません。

[Test]
public void FooPerformance_Bait()
{
    Assert.That(Time(()=>fooer.Foo()), Is.LessThanOrEqualTo(TimeSpan.FromSeconds(0));
}

次に、パフォーマンスの改善に取り組みます。暫定的な改善が行われるたびに、すべてのテスト(ピン留めとベイト)を実行します。成功すると、ベイティングテストの失敗出力で時間が減少し、ピン留めテストが失敗することはありません。

改善に満足したら、変更したコードのピン留めテストを更新し、ベイティングテストを削除します。

これらのテストで今何をしていますか?

最も心配の少ないことは、これらのテストに Explicit 属性のマークを付け、次にパフォーマンスをチェックするときのためにそれらを保持することです。

作業範囲の反対側では、これらの種類のテストを実行するためにCIで適切に制御されたサブシステムを作成することは、パフォーマンスの低下を監視するための非常に優れた方法です。私の経験では、実際の障害よりも「他の何かからのCPU負荷が原因でランダムに障害が発生する」という心配がたくさんあります。この種の取り組みの成功は、環境を管理する能力よりもチームの文化に依存します。

33
tallseth