ASP.NET アプリケーションのパフォーマンスを向上させる方法を試しています。私が検討していることの 1 つは、並列処理を使用し、操作を非同期にして、処理時間を短縮し、スループットを向上させることです。ページをレンダリングするために複数のデータベース取得を発行するという、私たちがかなり頻繁に行っていることをモックアップすることから始めました。
public ActionResult Index()
{
var dal = new Dal();
var cases = new List<Case>();
cases.AddRange( dal.GetAssignedCases() );
cases.AddRange( dal.GetNewCases() );
return View( "Cases", cases );
}
2 つの Dal メソッドを使用Thread.Sleep(2000)
してクエリをシミュレートし、ハードコードされたオブジェクトのコレクションを返します。これを Apache Bench を使用して実行するab -c 1 -n 1
と、約 4 秒かかります。それを改善しようとする私の最初の試みは:
public ActionResult Index()
{
var dal = new Dal();
var assignedCases = Task.Factory.StartNew( () => dal.GetAssignedCases() );
var newCases = Task.Factory.StartNew( () => dal.GetNewCases() );
IEnumerable<Case>[] allCases = Task.WhenAll( assignedCases, newCases ).Result;
return View( "Cases", allCases.SelectMany( c => c ) );
}
同じab
コマンドを使用してこれを実行すると、約 2 秒と表示されます。これは、それぞれ 2 秒かかる 2 つのタスクを実行しているが、それらが並行して実行されているためです。
ベンチマークを 10 個の同時リクエスト (つまりab -n 10 -c 10
) に変更すると、次のようになります。
Fulfilled Original Parallel
50% 4014 2038
66% 4015 2039
75% 4017 4011
100% までの残りの数値は、両方の列で類似しています。
ここで実行しているのは、スレッド プールの競合であると想定しています。リクエストの約 3 分の 2 はすぐに処理され、その後はスレッドがリクエストを処理するのを待っています。したがって、ミックスに非同期を追加すると、さらに多くのリクエストをより迅速に処理できるようになると思います。そこから問題が発生し始めますが、問題が長時間実行されるクエリをシミュレートする方法なのか、言語機能の使用方法なのか、それとも完全に間違った方向に進んでいるだけなのかわかりません。トンネルの先には対向列車が。:-)
最初にしたことは、DalAsync を作成することでした。DalAsync では、 を に置き換え、各メソッドをキーワードでマークThread.Sleep(2000)
し、戻り値の型を からに変更しました。次に、6 件のブログ投稿と MSDN 記事で読んだ情報を組み合わせて、新しいコントローラー メソッドを作成しました。await Task.Delay(2000)
async
IEnumerable<Case>
Task<IEnumerable<Case>>
public async Task<ActionResult> Index()
{
var dal = new DalAsync();
var assignedCases = dal.GetAssignedCasesAsync();
var newCases = dal.GetNewCasesAsync();
var allCases = await Task.WhenAll( assignedCases, newCases );
return View( "Cases", allCases.SelectMany( c => c ) );
}
これを使用して実行するab
と、1回のリクエストでもタイムアウトしてしまいます。また、次のバリエーションも試しました。これは機能しますが、元のバージョンとほぼ同じ数値を返します (クエリを再度シリアル化しているように見えるため、これは理にかなっています)。
var assignedCases = await dal.GetAssignedCasesAsync();
var newCases = await dal.GetNewCasesAsync();
var allCases = new List<Case>( assignedCases );
allCases.AddRange( newCases );
私がしたいことは次のとおりです。
- 2 つのクエリを並行して実行する
- コントローラーが Dal メソッドの応答を待機している場合、コントローラーはスレッドを解放し、他の要求を実行できるようにします。