2013-10-19 1 views
1
내가 비동기를 사용하여 작성하는 간단한 C# 콘솔 응용 프로그램을 사용하여 일부 벤치 마크를하고있는 중이 야

/의 구조를 기다리고 C# 5와 숫자가 추가하지 않습니다 (물론 실제로 그들이까지 추가하고, 그게 문제입니다;))벤치마킹 비동기 await를 몰이해

나는 세 가지 시나리오를 벤치마킹하고 있습니다 : 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(); 

이 70 %의 CPU 소비 평균 약 10초에을 완료한다.

2) 20K는 내가 사용하여 외부 웹 서버에 URL을 HttpClient를하고 비동기 방법뿐만 아니라 (PostAsync)를 호출이 시나리오에서는 간단한 HTTP 서버

호출합니다.

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 소비는 약 20 초 동안 평균 70 %, 나머지 20 초 동안 평균 30 %입니다. 벤치 마크 시나리오 # 3 40 초 걸리는 이유를 이해하지 않는 질문

지금

. 실행이 순차적이거나 시나리오 1 및 2의 CPU (또는 I/O)가 100 % 인 경우 시나리오 1의 타이밍 + 시나리오 2의 타이밍을 갖는 것이 정상이라고 말할 수 있습니다.

async/await 구조를 사용하여 완전히 비동기식으로 진행 중이며, 시나리오 # 3에서 예상했던 것은 시나리오 # 2의 지속 시간 인 30 초 ("체인의 가장 약한 링크") 내에 완료해야한다는 것입니다.

뭔가 여기가 이해가 안되는 :(

어떤 단서?

편집 : @svick의 요청에 따라, 여기에 내가 더 빨리보다 DB 전체에 대한 쿼리를 추측, (일부 쓸모없는 물건 제외) 벤치 마크의 전체 코드

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

벤치 마크의 전체 코드를 게시 할 수 있습니까? 특히 두 개의 비동기 메소드가 중요 할 수 있습니다. – svick

답변

0
시나리오 1의 결과로부터

및 2 HTTP 요청 (물론, 로컬 디스크는 인터넷보다 대기 시간이 짧기 때문에). 그래서 일반적으로 10 초 안에 완료됩니다. 하지만 제출하는 작업의 절반은 HTTP 요청이므로 절반의 계산 능력이 필요합니다. DB에 필요한 시간은 10 초에서 20 초로 두 배가됩니다. 이 시간 동안 CPU 사용량은 최대 달성 가능한 사용량 인 0.7입니다 (따라서 풀이 실제로 효과적입니다. 50 %의 작업 만 CPU의 30 % 만 사용하더라도 리소스 사용률을 극대화합니다).

그런 다음 DB 요청이 완료되고 HTTP 요청 만 남아 있습니다. DB와 HTTP가 동시에 실행되는 동안 리소스의 절반만이 HTTP 전용이므로 20 초는 10 초의 비보안 실행과 동일하므로 다른 30 초 ~ 10 초는 20 초를 실행합니다.

CPU 사용률이 100 %가 아니더라도 작업 관리자는 운영 체제를 손상시킬 수 있으므로 40k 스레드를 만들지 않습니다.

+0

답변 주셔서 감사합니다. DB 서버는 로컬 디스크가 아닌 외부 시스템입니다. 그러나 그것은 요점이 아닙니다.) async/await 구문을 사용 중이므로 다음 요청을 보내기 전에 DB 또는 HTTP의 응답을 기다리지 않습니다. DB/HTTP에 대한 40K 요청은 모두 즉시 전송됩니다. 달성 가능한 최대 사용률 인 CPU 사용량 0.7에 대해 어떻게 들으십니까? 1이 아니어야합니까? 어쨌든 나는 당신의 요점을 분명히 이해하려고 대답을 읽고 다시 읽습니다. – darkey

+0

비동기 요청을하는 경우 훨씬 쉽습니다. 기본적으로 모든 작업과 모든 요청 대기열을 신속하게 관리 할 수 ​​있습니다. 일부 ms 동안 100 % CPU 사용량을 달성하면 프로세스 응답 만 수행하면 CPU 부하를 결정하는 다른 끝점의 처리량이됩니다. 그리고 네트워크가 병목이기 때문에 처음 20 대에서는 100 %에 도달하지 않을까 걱정됩니다. –