2013-10-19 53 views
1

我正在做一些基準測試,使用一個簡單的C#控制檯應用程序編寫,使用C#5的async/await結構和數字不加起來(事實上它們合起來,這就是問題;))Benchmarking async await incomprehension

我是基準測試三種不同的場景: 1)20K調用SQL服務器存儲過程。 2)20K調用一個簡單的HTTP服務器 3)方案1)和2)一起

以下是有關方案的更多細節:

1)20K調用SQL存儲過程服務器。

在這種情況下,我調用外部SQL Server存儲過程20K次。 CallSqlStoredProcedureAsync方法使用ADO.NET異步方法(OpenAsync,ExecuteNonQueryAsync ...)。我甚至在方法入口處等待Task.Yield()以避免在到達異步點之前執行任何同步代碼,以避免在最短的時間內阻塞我的循環。

var tasks = new List<Task>(); 

for (int i=0; i<20000; i++) 
{ 
    tasks.Add(this.CallSqlStoredProcedureAsync()); 
} 

Task.WhenAll(tasks).Wait(); 

這樣就完成了約10秒與CPU消耗平均爲70%。

2)20K調用一個簡單的HTTP服務器

在這種情況下我呼籲外部Web服務器使用的HttpClient的url和異步方法以及(PostAsync)。

for (int i=0; i<20000; i++) 
{ 
    tasks.Add(this.SendRequestToHttpServerAsync()); 
} 

這樣就完成了約30秒與CPU消耗平均爲30%

3)方案1)和2)一起

for (int i=0; i<20000; i++) 
{ 
    tasks.Add(this.CallSqlStoredProcedureAsync()); 
    tasks.Add(this.SendRequestToHttpServerAsync()); 
} 

這樣就完成了約40秒,CPU消耗平均70%,持續約20秒,然後30秒,剩餘20秒。

現在的問題

我不明白爲什麼標杆正在40秒場景#3。如果執行是連續的,或者如果我的CPU(或I/O)對於方案1和方案2是100%,那麼我認爲這是正常的,具有方案1的時序+方案2的時序。

考慮到I我正在使用異步/等待結構完全異步,我期望的場景#3的目的是在30秒內完成(「鏈中最薄弱的環節」),即場景#2的持續時間。

有什麼我不明白在這裏:(

任何線索?

編輯:每@svick要求,這裏是基準的完整代碼(不包括一些無用的東西)

static void Main(string[] args) 
{ 
    var bench = new Bench();   

    while (true) 
    { 
     string iterationsAndScenario = Console.ReadLine(); 
     var iterations = int.Parse(iterationsAndScenario.Split(' ')[0]); 
     var scenario = int.Parse(iterationsAndScenario.Split(' ')[1]); 

     var sw = new Stopwatch(); 
     sw.Start(); 
     bench.Start(iterations, scenario).Wait(); 
     sw.Stop(); 

     Console.WriteLine("Bench too {0} ms", sw.EllapsedMilliseconds); 
    } 
} 

public class Benchmark 
{ 
    public Task Start(int iterations, int scenario) 
    { 
     var tasks = new List<Task>(); 

     if (scenario == 1) 
     { 
      for (int i=0; i<iterations; i++) 
      { 
       tasks.Add(this.CallSqlStoredProcedureAsync().ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted)); 
      } 
     } 
     else if (scenario == 2) 
     { 
      var httpClient = new HttpClient(); 

      for (int i=0; i<iterations; i++) 
      { 
       tasks.Add(httpClient.PostAsync(uri, new StringContent("Hello")).ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted)); 
      } 
     } 
     else if (scenario == 3) 
     { 
      var httpClient = new HttpClient(); 

      for (int i=0; i<iterations; i++) 
      { 
       tasks.Add(this.CallSqlStoredProcedureAsync().ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted)); 

       tasks.Add(httpClient.PostAsync(uri, new StringContent("Hello")).ContinueWith(t => Console.WriteLine(t.Exception), TaskContinuationOptions.OnlyOnFaulted)); 
      } 
     } 

     return Task.WhenAll(tasks); 
    } 

    public async Task CallSqlStoredProcedureAsync() 
    { 
     await Task.Yield(); 

     using (var conn = new SqlConnection(connectionString)) 
     { 
      using (var cmd = new SqlCommand("sp_mystoreproc", conn)) 
      { 
       cmd.CommandType = CommandType.StoredProcedure; 

       cmd.Parameters.AddWithValue("@param1", 'A'); 
       cmd.Parameters.AddWithValue("@param2", 'B'); 

       await cmd.Connection.OpenAsync(); 
       await cmd.ExecuteNonQueryAsync(); 
      } 
     } 
    } 
} 
+1

由於它使用線程池,是否達到工作線程數的默認限制,從而變得有效的順序?對於兩組並行運行的任務,它需要使可用線程翻倍。 – weston

+0

給ThreadPool.SetMaxThreads一個去。可以讓它接近100%cpu – weston

+0

你能發佈你的基準測試的完整代碼嗎?特別是兩種Async方法可能很重要。 – svick

回答

0

從情景1和2的結果,我想查詢到DB完整的速度比HTTP請求(當然,因爲本地磁盤的延遲低於互聯網)。因此,他們通常會在10秒內完成。但是你提交的一半任務是HTTP請求,所以你的一半計算能力很忙,DB需要的時間從10s增加到20s。在此期間,CPU使用率爲0.7,這是可實現的最大使用量(因此池實際上是有效的,因爲它可以最大限度地利用資源,即使50%的任務只使用30%的CPU)。

然後,數據庫請求完成,只保留HTTP請求。雖然DB和HTTP是同時執行的,但只有一半的資源專用於HTTP,所以這20個等於10個無爭議執行,這會使其他30s-10s = 20s執行。

即使CPU利用率不是100%,任務管理器也不會創建40k個線程,因爲這會破壞操作系統。

+0

感謝您的回答朱利奧。 數據庫服務器是外部機器,而不是本地磁盤。但是這不是重點;) 考慮到我使用async/await結構,我不等待來自數據庫或HTTP的響應,然後再發出下一個請求。所有40K的數據庫請求都會立即發送。 你聽到CPU使用率0.7是最大可實現的使用量?它不應該是1嗎? 無論如何,我正在閱讀並重新閱讀你的答案,試圖清楚地理解你的觀點。 – darkey

+0

如果您正在進行異步請求,則更容易。基本上,您可以快速設法排列所有任務和所有請求。在某些毫秒內達到100%的CPU使用率,那麼你所做的只是進程響應,而另一端點的吞吐量決定了你的CPU負載。而且我擔心在前20年內你沒有達到100%,因爲網絡是一個瓶頸。 –