2012-03-03 3 views
1

VS2010에서 병렬 패턴 라이브러리로 게임을 시작했습니다. 응용 프로그램에서 예상 한 결과를 얻었지만 디버그 버전과 릴리스 버전을 벤치 마크 할 때 다음과 같이 릴리스 버전에서 이상한 실행 시간이 발생합니다. 디버그 버전 : "순차 기간 : 1014" "병렬 기간 : 437" 릴리스 버전 "연속 재생 시간 : 31" "병렬 지속 시간 : 484"디버그 및 릴리스 버전에서 이상한 실행 시간

이 내 응용 프로그램 코드

double DoWork(int workload) 
{ 
    double result=0; 
    for(int i =0 ; i < workload;i++) 
    { 
     result +=sqrt((double)i * 4*3) + i* i; 
    } 
    return result; 
} 

vector<double> Seqential() 
{ 
    vector<double> results(100); 
    for(int i = 0 ; i <100 ; i++) 
    { 
     results[i] = DoWork(1000000); 
    } 

    return results; 
} 

vector<double> Parallel() 
{ 
    vector<double> results(100); 
    parallel_for(0,(int)100,1,[&results](int i) 
    { 
     results[i] = DoWork(1000000); 
    }); 

    return results; 
} 

double Sum(const vector<double>& results) 
{ 
    double result =0; 
    for(int i = 0 ; i < results.size();i++) 
     result += results[i]; 
    return result; 
} 

int main() 
{ 
    DWORD start = GetTickCount(); 
    vector<double> results = Seqential(); 
    DWORD duration = GetTickCount() - start; 
    cout<<"Sequential Duration : "<<duration <<" Result : " <<Sum(results) << endl; 

    start = GetTickCount(); 
    results = Parallel(); 
    duration = GetTickCount() - start; 
    cout<<"Prallel Duration : "<<duration <<" Result : " <<Sum(results) << endl; 
    system("PAUSE"); 
    return 0; 
} 

답변

1

문제는 Parallel에없는 것을 느리지 만 Seqential에있는 너무 빠른 :

  • Seqential에서, 컴파일러는 DoWork 항상 같은 결과를 얻을 것이다, 그래서 100 배를 호출하는 루프가 있음을보고 그리고 DoWork으로 끝나고으로 끝납니다.
  • 컴파일러는 똑같은 방법으로 parallel_for을 최적화 할만큼 충분히 똑똑하지 않으므로 실제 작업을 수행하게됩니다 (실제로는 번 100 번 더 실제 작업).

루프 카운터에 종속적 인 경우 DoWork을 호출하면 다른 호출에서 결과가 달라 지므로 호출이 중복되지 않으므로 컴파일러에서 최적화 할 수있는 방법이 없습니다. 릴리스 구성에서 ++ 2010 비주얼 C에 의해 구축 및 쿼드 코어 CPU에서 실행

#include <vector> 
#include <iostream> 
#include <math.h> 
#include <ppl.h> 
#include <Windows.h> 

using namespace std; 
using namespace Concurrency; 

double DoWork(int workload, int outer_i) 
{ 
double result=0; 
for(int i =0 ; i < workload;i++) 
{ 
    result +=sqrt((double)i * 4*3) + i* i; 
} 
result += outer_i; 
return result; 
} 

vector<double> Seqential() 
{ 
vector<double> results(100); 
for(int i = 0 ; i <100 ; i++) 
{ 
    results[i] = DoWork(1000000, i); 
} 

return results; 
} 

vector<double> Parallel() 
{ 
vector<double> results(100); 
parallel_for(0,(int)100,1,[&results](int i) 
{ 
    results[i] = DoWork(1000000, i); 
}); 

return results; 
} 

double Sum(const vector<double>& results) 
{ 
double result =0; 
for(int i = 0 ; i < results.size();i++) 
    result += results[i]; 
return result; 
} 

int main() 
{ 
DWORD start = GetTickCount(); 
vector<double> results = Seqential(); 
DWORD duration = GetTickCount() - start; 
cout<<"Sequential Duration : "<<duration <<" Result : " <<Sum(results) << endl; 

start = GetTickCount(); 
results = Parallel(); 
duration = GetTickCount() - start; 
cout<<"Prallel Duration : "<<duration <<" Result : " <<Sum(results) << endl; 
system("PAUSE"); 
return 0; 
} 

이 인쇄 : 예를 들어

BTW

Sequential Duration : 1607 Result : 1.68692e+015 
Prallel Duration : 374 Result : 1.68692e+015 

(, 당신은 정말 고려해야 더 나은 형식의 코드를 만들 수 있습니다.)

+0

귀하의 정보를 주셔서 감사합니다 지금은 내가 평행 시간의 절반의 순차에있어하지만 i5 프로세서에서 순차적으로보다 거의 4 시간 빠르다는 예외도 내 코드 형식에 대한 귀하의 노트를 말해 –

+0

@ Ma7moudEl - Naggar 일부 i5 프로세서는 2 코어 풀 4 코어와는 다른 4 개의 하이퍼 스레드가 있습니다. 그런 프로세서가 있니? BTW, 위의 측정은 2.5 GHz의 코어 2 쿼드에서 수행되었습니다. –

+1

@ Ma7moudEl-Naggar 코드 서식과 관련하여 코드를 들여 쓰기로 시작해야합니다. 또한 공백을 일관성있게 사용하는 것이 좋습니다 ... –

1

여러 스레드를 생성하는 오버 헤드가 단순히 결과를 계산하는 것보다 많은 시간이 소요될 수 있습니다. 릴리스 빌드에서 컴파일러는 많은 최적화 작업을 수행 할 수 있으므로 DoWork 내부에서 수행되는 작업량은 스레드를 설정하고 찢어 버리는 작업량에 비해 상당히 적습니다.

DoWork을 여러 번 반복하면 (예 : 반복적으로 반복) 기대에 더 근접한 결과가 표시됩니다.

+0

예. 그의 코드는 쓰레드 생성의 비용을 측정합니다. 놀랍지도 않지만 디버그 빌드에서 릴리스 빌드보다 훨씬 높습니다. –

+1

@ David Schwartz : 응?쓰레 디드 병렬 실행은 디버그보다 Release에서 *보다 * 걸립니다. 그것은 릴리스에서 거의 20 %까지 최적화를 해제하는 것과 같습니다. Sequential 실행은 스레드를 사용하지 않고 Release보다 Debug보다 30 배 빠릅니다. –

+1

작업을 늘리더라도 상대적인 성능 차이는 변하지 않습니다. 또한 스레드 수는 적습니다 (이론적으로 최대 100이지만 실제 코어 수와 동일합니다). 스레드 설정/문제 해 결에는 너무 작습니다. –

2

IIRC, C++ 11은 컴파일러가 컴파일시에 상수 표현식을 사전 계산하기 위해 함수로 깊숙이 들어갈 수있게합니다. sqrt과 같은 함수도 있습니다. 따라서 Sequential 버전은 결과 테이블까지 최적화 될 수 있습니다. 가능한 경우 Sequential에 대해 생성 된 어셈블리를보고 지나치게 단순화되었는지 또는 완전히 최적화되어 있는지 확인하는 것이 좋습니다.

컴파일시 계산할 수없는 DoWork에는 아무 것도 없습니다.

+0

'volatile '한정자를'workload' 또는'result'에 추가하면 도움이 될 수 있습니다. –

+0

컴파일러가 블러 프를 호출하지 않으면'volatile'을'i'에 추가하면 가장 큰 차이가납니다. –