2016-11-16 3 views
8

Я новичок в OpenMP, и я пытаюсь paralelize следующий код с помощью OpenMP:OpenMP paralelization ингибирует векторизации

#pragma omp parallel for 
for(int k=0;k<m;k++) 
{ 
    for(int j=n-1;j>=0;j--) 
    { 
     outX[k+j*m] = inB2[j+n * k]/inA2[j*n + j]; 

     for(int i=0;i<j;i++) 
     { 
      inB2[k*n+i] -= inA2[i+n * j] * outX[k + m*j]; 
     } 
    } 
} 

Paralelize внешний цикл довольно прямо вперед, но оптимизировать его, я хотел paralelize внутренний цикл (один итерация над i). Но когда я пытаюсь сделать это так:

#pragma omp parallel for 
for(int i=0;i<j;i++) 
{ 
    inB2[k*n+i] -= inA2[i+n * j] * outX[k + m*j]; 
} 

компилятор не векторизации внутреннего цикла («цикл версионным для векторизации из-за возможное наложение»), что делает его работать медленнее. Я скомпилировал его, используя gcc -ffast-math -std=c++11 -fopenmp -O3 -msse2 -funroll-loops -g -fopt-info-vec prog.cpp

Спасибо за любой совет!

EDIT: Я использую ключевое слово __restrict для массивов.

EDIT2: Интересно, что когда я сохраняю только прагму во внутреннем цикле и удаляю ее из внешнего, gcc будет ее векторизовать. Поэтому проблема возникает, когда я пытаюсь парализовать оба цикла.

EDIT3: Компилятор будет векторизовать цикл, когда я использую #pragma omp parallel для simd. Но он все еще медленнее, чем без параллелизма внутреннего контура.

+0

Легче векторизовать вручную, чем распараллеливать. Почему бы просто не сделать это? (и сохранить автоматическое распараллеливание) –

ответ

1

Спасибо всем за ответы. Мне удалось векторизовать внутренний цикл, используя #pragma omp parallel for simd, но программа была медленнее, чем без распараллеливания. В конце концов я нашел несколько иной алгоритм для решения проблемы, который намного быстрее. Спасибо за помощь, ребята!

+1

«VIPO» (Vectorize Inner, Parallel Outer) обычно лучше.Таким образом, чистый SIMD во внутреннем цикле (No 'parallel for'), а затем независимо от того, помогает ли параллелизм внешнему циклу, обычно является test-it-n-see endevour. В ситуациях с ограниченной пропускной способностью иногда помогает предварительная выборка, но у промахов в кеше может быть много ядер, которые мало выполняют реальную работу, тогда как одно ядро ​​может передавать данные беспрепятственно. Это редко можно понять правильно. – Holmz

1

Я предполагаю, что после того, как вы распараллелили внутреннюю петлю, ваш компилятор потерял дорожку inA2, inB2 и outX. По умолчанию предполагается, что любые области памяти, на которые указывают любые указатели, могут пересекаться друг с другом. На языке C в стандарте C99 введено ключевое слово restrict, в котором сообщается компилятору, что указатель указывает на блок памяти, который не указывается никаким другим указателем. У C++ нет такого ключевого слова, но, к счастью, у g++ есть соответствующее расширение. Поэтому попробуйте добавить __restrict в объявления указателей, затронутых циклом. Например, заменить

double* outX; 

с

double* __restrict outX; 
+0

К сожалению, это не тот случай. Я использую __restrict. Хорошо, однако, я добавлю его в ОП. –

1

Вы пробовали сделать внутренний цикл vecotorzed первый? а затем добавить параллельную часть (что может привести к снижению производительности в зависимости от промахов кэша)

#pragma omp parallel for 
for(int k=0;k<m;k++) 
{ 
    for(int j=n-1;j>=0;j--) 
    { 
     outX[k+j*m] = inB2[j+n * k]/inA2[j*n + j]; 
Q1 = k*n 
Q2 = n*j 
Q3 = m*j + k 
#pragma omp declare simd private(i,j,k,m,Q1,Q2,Q3) linear(i) uniform(outX,inA2,inB2) shared(inB2,inA2,outX) 
     for(int i=0;i<j;i++) 
     { 
      inB2[Q1+i] -= inA2[Q2+i] * outX[Q3]; 
     } 
    } 
} 

Он всегда принимает меня некоторое время, получая #pragma права с общей, общественностью и т.д. ... И я не проверял это.

+0

Спасибо за идею. Как я уже упоминал в EDIT3, я безумный, он векторизован, используя просто #pragma omp parallel для simd. Но он все еще медленнее, чем без паралелинизации внутреннего контура. –

+0

Это может указывать на ограниченность полосы пропускания, а не на ограничение вычислительной мощности. – Holmz