2017-05-12 7 views
0
auto t1 = chrono::steady_clock::now(); 
    #pragma omp parallel 
    { 

     for(int i=0;i<n;i++) 
     { 
      #pragma omp for collapse(2) 
      for(int j=0;j<n;j++) 
      { 

       for(int k=0;k<n;k++) 
       { 
        C[i][j]+=A[i][k]*B[k][j]; 
       } 

      } 
     } 
    } 
auto t2 = chrono::steady_clock::now(); 

auto t = std::chrono::duration_cast<chrono::microseconds>(t2 - t1).count(); 

변수 t는 상당히 일정하게 유지됩니다. 왜 이런 일이 일어나고 있는지 잘 모르겠습니다. 또한 잠시 동안 t가 0으로 출력됩니다. 또 하나의 문제는 n의 값을 500과 같이 증가 시키면 컴파일러가 프로그램을 실행할 수 없다는 것입니다. (여기서 n = 100을 사용했습니다) GNU GCC 컴파일러에서 code :: 블록을 사용하고 있습니다.행렬 곱셈의 성능은 C++의 OpenMP에서 변경되지 않습니다.

+1

외부 루프의 병렬 처리에 유리하게 보입니다. 축소 된 내부 루프를 포함하여 다른 스레드가 암시하는 것처럼 스레드 내에서 중요한 최적화가 제외 될 수 있습니다. 처음에는 단일 스레드를 최적화하지 않고 병렬 처리를 시작하면 어쨌든 제한이 있습니다. – tim18

답변

1

제안 된 OpenMP 병렬 처리가 올바르지 않아 잘못된 결과가 발생할 수 있습니다. collapse(2)을 지정할 때 스레드는 (j, k) 반복을 "동시에"실행합니다. 두 개 이상의 스레드가 같은 j이지만 다른 k에서 작동하면 A[i][k]*B[k][j]의 결과가 동일한 배열 위치 C[i][j]에 누적됩니다. 이것은 "경합 조건"이라고합니다. 즉, "두 개 이상의 스레드가 공유 데이터에 액세스 할 수 있으며 동시에 변경하려고합니다"(What is a race condition?). 코드가 OpenMP 유효하지 않고 여러 요인 (스케줄링, 컴파일러 구현, 스레드 수 등)에 따라 잘못된 결과가 발생할 수 있지만 데이터 경주가 반드시 잘못된 결과로 이어지는 것은 아닙니다. 위의 코드에서 문제를 해결하려면, OpenMP의는 reduction 절을 제공합니다 :

#pragma omp parallel 
    { 
     for(int i=0;i<n;i++) { 
      #pragma omp for collapse(2) reduction(+:C) 
      for(int j=0;j<n;j++) { 
       for(int k=0;k<n;k++) { 
        C[i][j]+=A[i][k]*B[k][j]; 

개인 사본 각 암시 적 작업 (...)에서 생성되고 감소의 초기화 값으로 초기화됩니다 "그래서 -identifier. 영역이 끝나면 원래 목록 항목은 축소 식별자와 연결된 결합자를 사용하여 개인 복사본의 값으로 업데이트됩니다 "(http://www.openmp.org/wp-content/uploads/openmp-4.5.pdf). C의 배열 축소는 OpenMP 4.5부터 표준에 의해 직접 지원됩니다 (컴파일러가 지원하는지 확인하십시오. 그렇지 않은 경우이를 구현하기위한 오래된 수동 방법이 있습니다, Reducing on array in OpenMp).

그러나, 주어진 코드, 가장 안쪽의 루프의 병렬화를 피하기 위해 아마 더 적합해야 감소는 전혀 필요하지 않도록 :

#pragma omp parallel 
    { 
     #pragma omp for collapse(2) 
     for(int i=0;i<n;i++) { 
      for(int j=0;j<n;j++) { 
       for(int k=0;k<n;k++) { 
        C[i][j]+=A[i][k]*B[k][j]; 

직렬가 OpenMP의 버전보다 빠를 수 있습니다 작은 크기의 행렬 및/또는 적은 수의 스레드. 인텔 컴퓨터에서 최대 16 개의 코어 (n = 1000), GNU 컴파일러 v6.1을 사용하는 경우 -O3 최적화가 활성화되면 약 4 코어가 중단되고 -O0으로 컴파일되는 약 2 코어가 중단됩니다. 성능 손실이 극적인 (반전 속도 업)입니다 감소를 사용

Serial  418020 
----------- WRONG ORIG -- +REDUCTION -- OUTER.COLLAPSE -- OUTER.NOCOLLAPSE - 
OpenMP-1 1924950  2841993  1450686   1455989 
OpenMP-2 988743  2446098   747333   745830 
OpenMP-4 515266  3182262   396524   387671 
OpenMP-8 280285  5510023   219506   211913 
OpenMP-16 2227567  10807828   150277   123368 

: 명확하게하기 위해 나는 측정 된 성과를보고한다. 외부 병렬 처리 (w 또는 o/붕괴)가 가장 좋은 옵션입니다.

큰 행렬에 대한 실패와 관련하여 가능한 원인은 사용 가능한 스택의 크기와 관련이 있습니다. 시스템 및 OpenMP 스택 크기를 모두 확대 해보십시오. 즉,

ulimit -s unlimited 
export OMP_STACKSIZE=10000000 
+0

자세한 답변을 주셔서 감사합니다.하지만 사실은 내가 축소를 사용하지 않았다는 사실에도 불구하고 정확한 답변을 얻습니다. 또한, n이 프로세서 갯수보다 훨씬 클 때 붕괴가 필요하지 않을 수도 있습니다. 그래도 필자는 시리얼 프로그램보다 약간의 성능 향상을 가져야한다고 주장했다. 또한 n을 500과 같이 사용하면 프로그램이 중단됩니다. 그 이유는 무엇일까요? – Sanit

+0

C++에서 크기를 어떻게 변경합니까? omp 스택 크기를 변경하는 방법을 찾을 수 없으며 'libgomp : Thread creation failed : Resource temporarily unavailable'오류가 발생합니다. – Sanit

+0

OMP_STACKSIZE의 작은 값을 사용하십시오. 내 컴퓨터에서 위의 숫자는 작동하지만 컴퓨터에 따라 다릅니다. – Franz

0

jdivide/mod operations을 사용하여 다시 생성되기 때문에 collapse 지시문이 실제로 담당 할 수 있습니다.

collapse없이 시도하셨습니까?

+0

귀하의 제안은 효과가 있었지만 아직 붕괴를 사용하여 잘못된 점을 이해하지 못했습니다.또한, n = 500과 같은 것으로 가져 가면 내 프로그램이 왜 충돌합니까? – Sanit