web-dev-qa-db-ja.com

負荷テスト:秒あたりのリクエストを生成する方法?

Zeroc-ICEで動作するサーバーコンポーネントがあります。負荷テストをしたいときは、並列ライブラリを使用して複数のリクエストを作成することで実現できると思いました。しかし、それはそのようになってしまいます。 Parallel(For)を使用する C#のライブラリは明らかに簡単でしたが、同じ瞬間にすべてを並列に正確に生成しているようには見えません。したがって、1秒あたりN個のリクエストを作成するための定義にはなりません。どうすればよいですか?最初に負荷テストをしたい人は、実際にこれについて考えるでしょう。

  1. 実際に毎秒N個のリクエストを実際に作成する効率的な方法は何ですか?

  2. 別の神話は、並列プログラミングについてです。一般にC#または.Netで並列プログラミングパターンを使用したことがある場合は、教えてください。 5つのプロセスがあるとします。 5つのプロセスすべてを同時に開始する方法。私のリソースの消費にはどういう意味がありますか?私はネットで入手できる多くの資料を読んでみましたが、私の質問に対する答えであるよりも、ますます多くの質問を受けます。

  3. Parallel.Forを使用して、N個のスレッドを作成し、時間を測定しました。次に、タスクの列挙にTask.Factory.startを使用して同じことを試しました。測定された時間は異なっていました。では、これらを使用することの違いは何ですか?対応するクラスを使用する必要があるのはいつですか?私たちは多くの富を持っていることがよくありますが、それは私たちがお互いを区別する方法を正確に知らないということです。これは私にとってはそのようなケースの1つであり、私が別のものを使用すべきでない理由を見つけることができません。

  4. 私はストップウォッチクラスを使用して、最高だと主張するこれらの時間を測定しました。コンポーネントの負荷テストのシナリオでは、応答時間を測定する方法は何でしょうか。ストップウォッチは私にとって最良の解決策のようです。どんな意見でも大歓迎です。

ps:Webアプリケーション用の多くの負荷テストツールがあります。鉱山はサーバーコンポーネントのカスタマイズされたケースです。そして、私の質問は、毎秒Nスレッドを作成することに関するものです。

すべての意見を歓迎します。それはプログラミングの問題ではないので、あまり考えないでください。もちろん、そうです。QEを自分で行い、プログラマーが製品のパフォーマンスを直接知りたいプログラマにとって、ベルは鳴るはずです。自分で。私は多くのオプションを試しましたが、実際にどうすればいいですか?

14
King

答えが全部わからない。うまくいけば、いくつかに光を当てることができます。

.NETのスレッドモデルに関する以前のステートメントを簡略化するには、Parallel Libraryがタスクを使用し、タスクのデフォルトのTaskSchedulerがThreadPoolを使用することを知っているだけです。階層の上位(ThreadPoolが一番下)に行くほど、アイテムの作成時にオーバーヘッドが大きくなります。余分なオーバーヘッドがあったからといって遅くなるわけではありませんが、そこにあることを知っておくのは良いことです。最終的に、マルチスレッド環境でのアルゴリズムのパフォーマンスは、その設計にかかっています。順調に機能するものは、並行して機能しない場合があります。多くの要因が関与しているため、厳格かつ迅速なルールを提供することはできません。それらは、実行しようとしていることに応じて変化します。ネットワークリクエストを処理しているので、少し例を挙げて説明します。

私がソケットの専門家ではないことを述べさせてください。ゼロック・アイスについてはほとんど何も知りません。私は非同期操作について少し知っています、そしてこれはそれが本当にあなたを助ける場所です。ソケットを介して同期リクエストを送信する場合、Socket.Receive()を呼び出すと、リクエストが受信されるまでスレッドがブロックされます。これは良くありません。ブロックされているため、スレッドはこれ以上リクエストを行うことができません。 Socket.Beginxxxxxx()を使用すると、I/O要求が行われ、ソケットのIRPキューに入れられ、スレッドは続行します。つまり、スレッドは実際には何もブロックせずに、ループ内で何千もの要求を行うことができます。

私があなたを正しく理解している場合、テストコードでZeroc-Iceを介した呼び出しを使用しており、実際にはhttpエンドポイントに到達しようとしていません。その場合は、Zeroc-Iceの仕組みがわからないことを認めます。ただし、 ここに記載されているアドバイス 、特にConsider Asynchronous Method Invocation (AMI)の部分に従うことをお勧めします。ページはこれを示しています:

AMIを使用することにより、クライアントは、呼び出しが送信されると(または、すぐに送信できない場合はキューに入れられると)すぐに制御のスレッドを取り戻し、クライアントはそのスレッドを使用して、その間に他の有用な作業を実行できます。 。

これは、.NETソケットを使用して上記で説明したものと同等のようです。たくさんの送信を行おうとするときにパフォーマンスを改善する他の方法があるかもしれませんが、私はここから、またはそのページにリストされている他の提案から始めます。あなたはあなたのアプリケーションのデザインについて非常に漠然としていたので、私は上記よりも具体的にすることができます。覚えておいてください、絶対に必要なスレッドより多くのスレッドを使用しないでください。そうしないと、アプリケーションの実行速度が必要以上に遅くなる可能性があります。 。

擬似コードのいくつかの例(私が実際に学習する必要なしに、可能な限り氷に近づけようとしました):

var iterations = 100000;
for (int i = 0; i < iterations; i++)
{
    // The thread blocks here waiting for the response.
    // That slows down your loop and you're just wasting
    // CPU cycles that could instead be sending/receiving more objects
    MyObjectPrx obj = iceComm.stringToProxy("whateverissupposedtogohere");
    obj.DoStuff();
}

より良い方法:

public interface MyObjectPrx : Ice.ObjectPrx
{
    Ice.AsyncResult GetObject(int obj, Ice.AsyncCallback cb, object cookie);
    // other functions
}

public static void Finished(Ice.AsyncResult result)
{
    MyObjectPrx obj = (MyObjectPrx)result.GetProxy();
    obj.DoStuff();
}

static void Main(string[] args)
{
    // threaded code...
    var iterations = 100000;
    for (int i = 0; i < iterations; i++)
    {
        int num = //whatever
        MyObjectPrx prx = //whatever
        Ice.AsyncCallback cb = new Ice.AsyncCallback(Finished);
        // This function immediately gets called, and the loop continues
        // it doesn't wait for a response, it just continually sends out socket
        // requests as fast as your CPU can handle them.  The response from the
        // server will be handled in the callback function when the request
        // completes.  Hopefully you can see how this is much faster when 
        // sending sockets.  If your server does not use an Async model 
        // like this, however, it's quite possible that your server won't 
        // be able to handle the requests
        prx.GetObject(num, cb, null);
    }
}

より多くのスレッド!=ソケットを送信しようとする場合(または実際に何かを行う場合)のパフォーマンスが向上することに注意してください。スレッドは、作業中の問題を自動的に解決するという点で魔法ではありません。スレッドが待機時間の多くを費やしていない限り、理想的にはコアあたり1つのスレッドが必要です。コンテキストの切り替えが発生し、リソースが浪費されるため、各リクエストを独自のスレッドで実行することはお勧めできません。 (私がそれについて書いたすべてを表示したい場合は、[編集]をクリックして、この投稿の過去のリビジョンを確認してください。手元にある主要な問題を曇らせるだけのようだったため、削除しました。)

1秒間に多数のリクエストを行う場合は、これらのリクエストをスレッドで確実に行うことができます。ただし、スレッドの作成をやり過ぎないでください。バランスを見つけて、それに固執します。非同期モデルと同期モデルを使用すると、パフォーマンスが向上します。

お役に立てば幸いです。

10

私は質問1)をスキップして、#2に進みます。これは、一般に、探していることを達成するための許容できる方法だからです。以前は、1秒あたりnメッセージを達成するために、次に起動する単一プロセスを作成できますpAppDomains。各AppDomainは基本的に、特定の時点に到達すると(タイマーを使用して)要求ループの実行を開始します。この時間は、各AppDomainで同じである必要があります。これらは、同じ時点でサーバーにヒットし始めることを保証するためです。

このような何かがあなたのリクエストを送信するために機能するはずです:

WaitCallback del = state => 
{ 
    ManualResetEvent[] resetEvents = new ManualResetEvent[10000]; 
    WebClient[] clients = new WebClient[10000]; 

    for (int index = 0; index < 10000; index++) 
    { 
        resetEvents[index] = new ManualResetEvent(false); 
        clients[index] = new WebClient(); 

        clients[index].OpenReadCompleted += new OpenReadCompletedEventHandler (client_OpenReadCompleted); 

        clients[index].OpenReadAsync(new Uri(@"<REQUESTURL>"), resetEvents[index]); 
    } 

    bool succeeded = ManualResetEvent.WaitAll(resetEvents, 10000); 
    Complete(succeeded); 

    for (int index = 0; index < 10000; index++) 
    { 
        resetEvents[index].Dispose(); 
        clients[index].Dispose(); 
    } 
}; 

while(running)
{
    ThreadPool.QueueUserWorkItem(del);
    Thread.Sleep(1000);
}

これにより、実行しているマシンのパフォーマンスが低下する可能性があるため、リソースがあれば(アプリドメインの代わりにプロセスを使用して)、常に複数の異なるマシンから同様のタイプのループを実装できます。

3番目の質問については、このリンクを読んでください http://www.albahari.com/threading/

最後に、ストップウォッチをヒットカウンターと組み合わせて、サーバーでの継続時間と一意のヒットの両方を追跡する必要があります。これにより、事後に分析を行うことができます。

2
Chris

純粋にXスレッドすべてがリソースに同時にヒットするようにしたい場合は、各スレッドをカウントダウンラッチの後ろに配置して、セマフォチェック間の短い待機時間を指定できます。

C#には実装があります(http://msdn.Microsoft.com/en-us/library/system.threading.countdownevent(VS.100).aspx)。

同時に、システムのストレステストを行っている場合は、実際に競合状態も確認する必要があるかもしれません。その場合、ランダムな頻度とピーク/フルラフで時間とともに変化するスレッドごとにスレッドスリープ期間を設定する必要があります。

同様に、実際には複数のリクエストを迅速に送信したくない場合があるかもしれません。サーバーを悪い状態にしたり、メッセージの消費や送信により多くの時間を費やすスレッドの数を少なく設定することで、実際のパフォーマンスをテストした方がよいでしょう。サーバーはおそらく低速の進行中のメッセージを処理するために独自のスレッドを起動する必要があるため、ソケットを介してやり取りします。

0
Keith Brings

.NETプロジェクトの負荷テストを実現する最も簡単な方法は、Visual StudioのUltimateエディションを購入することです。これには、負荷テストを含むあらゆる種類のテストの実行を支援する統合テストツールが付属しています。負荷テストは、1台のPCで仮想ユーザーを作成するか、多数のユーザーのために複数のユーザーに分散することで実行できます。また、ターゲットサーバーにインストールして、テスト中に追加のデータを返す小さなプログラムもあります。

ただし、これは高価ですが、究極のエディションには多くの機能が付属しているため、すべてを使用すると、よりリーズナブルな価格になります。

0
Ryathal

Nがかなり小さい場合は、スレッドを気にしないでください。 1秒あたりN個のリクエストを生成するには、実時間(DateTime.Now)。リクエストの前後の両方に時間をかけ、次にSleepを追加して次のリクエストを遅らせます。

たとえば、N = 5(200 ms)の場合:

Before request: 12:33:05.014
After request: 12:33:05.077
Sleep(137)
Before request: 12:33:05.214
After request: 12:33:05.271
Sleep(131)

これは完璧ではありません。 Sleepは正確ではない場合があります。偏差の実行カウントを維持し(X番目の要求の前に、時間はX-1/Nになるはずです)、それに応じてスリープ期間を調整できます。

Nが大きくなりすぎたら、Mスレッドを作成し、各スレッドに同じ方法でN/Mリクエストを生成させます。

0
MSalters