2013-03-10 3 views
2

이 코드는 OpenMP에서 느립니다. OpenMP가 없으면 약 10 초가됩니다. OpenMP를 사용하면 약 40 대가됩니다. 무슨 일 이니? 대단히 감사합니다!병렬 처리 방법이 느린 OpenMP 코드?

for (i=2;i<(nnoib-2);++i){ 
    #pragma omp parallel for 
    for (j=2; j<(nnojb-2); ++j) { 
     C[i][j]= absi[i]*absj[j]* 
       (2.0f*B[i][j] + absi[i]*absj[j]* 
       (VEL[i][j]*VEL[i][j]*fat* 
       (16.0f*(B[i][j-1]+B[i][j+1]+B[i-1][j]+B[i+1][j]) 
       -1.0f*(B[i][j-2]+B[i][j+2]+B[i-2][j]+B[i+2][j]) 
       -60.0f*B[i][j] 
       )-A[i][j])); 
     c2 = (abs(C[i][j]) > Amax[i][j]); 
     if (c2) { 
      Amax[i][j] = abs(C[i][j]); 
      Ttra[i][j] = t; 
     } 
    } 
} 
+0

'c2'는 어디에 선언 되었습니까? – Mysticial

+0

c2는 전역 변수입니다. c2는 int입니다. 감사! –

+1

그런 다음 'c2'에 대한 경쟁 조건/종속성이 있기 때문에 로컬로 만듭니다. – Mysticial

답변

3

OpenMP를 사용한다고해서 프로그램이 더 빨리 실행되는 것은 아닙니다. 각 스레드를 산란에 관련된 비용이 있습니다

  1. 하고있는 경우 계산의 작은 금액 자체가 더 많은 시간이 걸릴 것입니다 스레드의 산란을 할 수있는 스레드를 생성 : 몇 가지 여기 일이 될 수있다 계산보다.

  2. 기본적으로 OpenMP는 CPU에서 지원하는 최대 스레드 수를 생성합니다. 코어 당 2 개 이상의 스레드를 지원하는 CPU를 사용하면 각 코어의 리소스에 대해 스레드가 경쟁하게됩니다. omp_get_num_threads()을 사용하면 기본적으로 생성 될 스레드 수를 확인할 수 있습니다. omp_set_num_threads()을 사용하여 값의 절반으로 코드를 실행하는 것이 좋습니다.

결과가 OpenMP를 사용하거나 사용하지 않았 음을 확인 했습니까? 변수 j 및 c2와의 종속성이있는 것 같습니다. 각 스레드에 개인 선언해야합니다 : 어떤 병렬화를 시도하기 전에, 당신은 코드가 이미 최적화되어 있는지 확인해야합니다 :

#pragma omp parallel for private(j,c2) 

내가 다른 일을 추가하고 싶었다.

는 컴파일러, 컴파일러 플래그 및 명령의 복잡도에 따라, 컴파일러 또는 코드를 최적화하지 않을 수 있습니다 : 그것은 많이 보이지 않을 수도

// avoid calculation nnoib-2 every iteration 
int t_nnoib = nnoib - 2; 
for (i=2; i< t_nnoib; ++i){ 
    // avoid calculation nnojb-2 every iteration 
    int t_nnojb = nnojb - 2; 
    // avoid loading absi[i] every iteration 
    int t_absi = absi[i]; 
    for (j=2; j< t_nnojb; ++j) { 
     C[i][j]= t_absi * absj[j] * 
      (2.0f*B[i][j] + t_absi * absj[j] * 
      (VEL[i][j] * VEL[i][j] * fat * 
      (16.0f * (B[i][j-1] + B[i][j+1] + B[i-1][j] + B[i+1][j]) 
       -1.0f * (B[i][j-2] + B[i][j+2] + B[i-2][j] + B[i+2][j]) 
       -60.0f * B[i][j] 
      ) - A[i][j])); 

     // c2 is a useless variable 
     if (abs(C[i][j]) > Amax[i][j]) { 
      Amax[i][j] = abs(C[i][j]); 
      Ttra[i][j] = t; 
     } 
    } 
} 

를, 그러나에 큰 영향을 미칠 수 있습니다 암호. 컴파일러는 레지스터에 로컬 변수를 배치하려고 시도합니다 (액세스 시간이 훨씬 빠름). 제한된 수의 레지스터가 있기 때문에 무기한으로이 기술을 적용 할 수 없으며이를 악용하면 코드가 레지스터 누출로 고통받을 수 있습니다.

absi 어레이의 경우 j 루프를 실행하는 동안 시스템이 해당 어레이 조각을 캐시에 보관하지 않아도됩니다. 이 기법의 일반적인 개념은 내부 루프의 변수에 의존하지 않는 모든 배열 액세스를 외부 루프로 이동하는 것입니다. 크리스티아누 언급 비용 외에도

+0

좋습니다! 나는 수정 될 것이다. 나는 1000과 같고, j도 1000과 같습니다. 매우 고맙습니다. –

2

, 당신의 선택은 j 루프를 통해보다는 i 루프를 통해 병렬화 할 수는 C, Amax, Ttra, 할당되는 세 개의 배열에서 거짓 공유의 위험을 초래할 수 있습니다. 기본적으로 한 스레드가 배열 중 하나의 요소에 쓰면 동일한 캐시 행의 인접 요소도 해당 코어의 캐시에로드됩니다. 그런 다음 다른 코어가 자체 항목을 다른 항목에 쓰게되면 여러 캐시가 잠재적으로 '줄다리기'를 진행하면서 다른 캐시에서 해당 줄을 가져와야합니다.

이 해결 방법은 j 이상의 내부 루프 대신 i 이상의 외부 루프를 병렬 처리하는 것입니다. 루프는 i 루프를 반복 할 때마다 생성되는 것이 아니라 스폰 및 작업 할당이 한 번만 발생하기 때문에 Cristiano의 대답에서 언급 한 비용도 대폭 절감 할 수 있습니다.jc2의 민영화가 필요하며, 그 다음으로 ifc2의 값을 인라인하고 댓글에 설명 된대로 변수를 삭제해야합니다. 효율성을 높이기 위해 j 대신 로컬 변수를 가변적으로 사용하면 스레드 전용 변수에 액세스하지 않아도됩니다.

(중요 함) 검사와 마찬가지로이 루프 중첩은 실제로 프로그램에서 측정 한 시간의 대부분을 차지합니다. OpenMP 플러그인을 추가하면 시간이 10 세 이하에서 40 세 이하로 변경되었습니다.

+0

이 답변의 단락 2에서 OpenMP 구현은 일반적으로 실행 중에 스레드를 동적으로 작성 및 파괴하지 않고 시작시 스레드를 작성하고 종료시이를 제거합니다. 이는 OpenMP가 원래 의도했던 일반적인 용도에 적합하며 다목적 스레딩 시스템이 아닙니다. –

+0

좋은 구현은 프로그래머가 요청한 것과 일치 할 의미를 얻을 수있을 때이를 수행합니다. 그럼에도 불구하고 각 반복에서 마스터 스레드에서 작업자에게 작업을 전달할 때 오버 헤드가 있습니다 (스케줄링 전략에 따라 무시할 수 없을 정도로). – Novelocrat