Почему компилятор игнорирует прагмы OpenMP? - PullRequest
1 голос
/ 25 января 2011

В следующем C-коде я использую OpenMP во вложенном цикле.Поскольку возникает состояние гонки, я хочу выполнить атомарные операции в конце:

double mysumallatomic() {

  double S2 = 0.;
  #pragma omp parallel for shared(S2)
  for(int a=0; a<128; a++){
    for(int b=0; b<128;b++){
      double myterm = (double)a*b;
      #pragma omp atomic
      S2 += myterm;
    }
  }
  return S2;
}

Дело в том, что #pragma omp atomic не влияет на поведение программы, даже если я ее удалю, ничего не произойдет.Даже если я изменю его на #pragma oh_my_god, я не получу ошибку!

Интересно, что здесь не так, могу ли я сказать компилятору быть более строгим при проверке прагм omp или почему я не получаюошибка, когда я делаю последнее изменение

PS: для компиляции я использую:

gcc-4.2 -fopenmp main.c functions.c -o main_elec_gcc.exe

PS2: новый код, который дает мне ту же проблему и основанный на идее gillespie:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <omp.h>
#include <math.h>

#define NRACK 64
#define NSTARS 1024

double mysumallatomic_serial(float rocks[NRACK][3], float moon[NSTARS][3],
                             float qr[NRACK],float ql[NSTARS]) {
  int j,i;
  float temp_div=0.,temp_sqrt=0.;
  float difx,dify,difz;
  float mod2x, mod2y, mod2z;
  double S2 = 0.;

  for(j=0; j<NRACK; j++){
    for(i=0; i<NSTARS;i++){     
      difx=rocks[j][0]-moon[i][0];
      dify=rocks[j][1]-moon[i][1];
      difz=rocks[j][2]-moon[i][2];
      mod2x=difx*difx;
      mod2y=dify*dify;
      mod2z=difz*difz;
      temp_sqrt=sqrt(mod2x+mod2y+mod2z);
      temp_div=1/temp_sqrt;
      S2 += ql[i]*temp_div*qr[j];       
    }
  }
  return S2;
}

double mysumallatomic(float rocks[NRACK][3], float moon[NSTARS][3], 
                      float qr[NRACK],float ql[NSTARS]) {
  float temp_div=0.,temp_sqrt=0.;
  float difx,dify,difz;
  float mod2x, mod2y, mod2z;
  double S2 = 0.;

  #pragma omp parallel for shared(S2)
  for(int j=0; j<NRACK; j++){
    for(int i=0; i<NSTARS;i++){
      difx=rocks[j][0]-moon[i][0];
      dify=rocks[j][1]-moon[i][1];
      difz=rocks[j][2]-moon[i][2];
      mod2x=difx*difx;
      mod2y=dify*dify;
      mod2z=difz*difz;
      temp_sqrt=sqrt(mod2x+mod2y+mod2z);
      temp_div=1/temp_sqrt;
      float myterm=ql[i]*temp_div*qr[j];    
      #pragma omp atomic
      S2 += myterm;
    }
  }
  return S2;
}
int main(int argc, char *argv[]) {
  float rocks[NRACK][3], moon[NSTARS][3];
  float qr[NRACK], ql[NSTARS];
  int i,j;

  for(j=0;j<NRACK;j++){
    rocks[j][0]=j;
    rocks[j][1]=j+1;
    rocks[j][2]=j+2;
    qr[j] = j*1e-4+1e-3;
    //qr[j] = 1;
  }

  for(i=0;i<NSTARS;i++){
    moon[i][0]=12000+i;
    moon[i][1]=12000+i+1;
    moon[i][2]=12000+i+2;
    ql[i] = i*1e-3 +1e-2 ;
    //ql[i] = 1 ;
  }
  printf(" serial: %f\n", mysumallatomic_serial(rocks,moon,qr,ql));
  printf(" openmp: %f\n", mysumallatomic(rocks,moon,qr,ql));
  return(0);
}

Ответы [ 4 ]

3 голосов
/ 25 января 2011
  1. Использование флага -Wall выделяет ошибки прагмы. Например, когда я неправильно пишу atomic, я получаю следующее предупреждение.

    main.c:15: warning: ignoring #pragma omp atomic1

  2. Я уверен, что вы знаете, но на всякий случай ваш пример должен быть обработан с помощью reduction

  3. Когда вы используете omp parallel, по умолчанию все переменные используются совместно. Это не то, что вы хотите в вашем случае. Например, каждый поток будет иметь различное значение difx. Вместо этого ваш цикл должен быть:

    #pragma omp parallel for default(none),\
    private(difx, dify, difz, mod2x, mod2y, mod2z, temp_sqrt, temp_div, i, j),\
    shared(rocks, moon, ql, qr), reduction(+:S2)
    for(j=0; j<NRACK; j++){
      for(i=0; i<NSTARS;i++){
        difx=rocks[j][0]-moon[i][0];
        dify=rocks[j][1]-moon[i][1];
        difz=rocks[j][2]-moon[i][2];
        mod2x=difx*difx;
        mod2y=dify*dify;
        mod2z=difz*difz;
        temp_sqrt=sqrt(mod2x+mod2y+mod2z);
        temp_div=1/temp_sqrt;
        S2 += ql[i]*temp_div*qr[j];  
      }
    }
    
1 голос
/ 05 января 2017

Я знаю, что это старый пост, но я думаю, что проблема в том, что порядок параметров gcc, -fopenmp должен быть в конце строки компиляции.

0 голосов
/ 30 декабря 2015

У вас есть

#pragma omp parallel for shared(S2)
  for(int a=0; a<128; a++){
   ....

Таким образом, единственное распараллеливание будет с циклом for.

Если вы хотите получить атом или сокращение, вы должны сделать

#pragma omp parallel 
{
 #pragma omp for shared(S2)
   for(int a=0; a<128; a++){
     for(int b=0; b<128;b++){
       double myterm = (double)a*b;
       #pragma omp atomic
        S2 += myterm;
     } // end of second for
   } // end of 1st for
} // end of parallel code
return S2;
} // end of function

В противном случае все, что после # будет комментировано

0 голосов
/ 25 января 2011

Во-первых, в зависимости от реализации, сокращение может быть лучше, чем использование атомарного. Я бы попробовал и то, и другое, чтобы убедиться в этом.

Во-вторых, если вы откажетесь от атомного, вы можете увидеть или не увидеть проблему (неправильный результат), связанную с расой. Все дело в сроках, которые от одного пробега к другому могут быть совершенно разными. Я видел случаи, когда результат был неправильным только один раз в 150 000 пробежек, и другие, где он был неправильным все время.

В-третьих, идея прагм заключалась в том, что пользователю не нужно знать о них, если они не имеют эффекта. Кроме того, философия Unix (и его производных) заключается в том, что он работает тихо, если нет проблем. Сказав это, многие реализации имеют своего рода флаг, чтобы пользователь мог получить больше информации, потому что он не знал, что происходит. Вы можете попробовать -Wall с gcc, и, по крайней мере, он должен пометить прагму oh_my_god как игнорируемую.

...