2011-01-25 2 views
1

В следующем C-коде я использую OpenMP во вложенном цикле. Так происходит состояние гонки, я хочу, чтобы выполнить атомарные операции в конце:Почему компилятор игнорирует прагмы 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); 
} 

ответ

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

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

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

  3. При использовании OMP параллельно, по умолчанию для всех переменных, которые будут совместно , Это не то, что вы хотите в своем деле. Например, каждый поток будет иметь другое значение 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]; 
        } 
    } 
    
+0

привет, 2) да, я использовал перед тем снижение, но такой же проблема! 3) так, кажется, что ни сокращение, ни атомная помощь здесь, что происходит? – flow

+0

@Werner: Когда я использую атом, я всегда получаю правильный ответ. Я добавлю свой код в свой ответ – csgillespie

+0

, ваша программа работает эффективно. Я понял, что моя предыдущая ошибка была связана с тем, что внутри параллели необходимо определить «a» и «b», и для этого нам нужно использовать компилятор g ++. До того, как я использовал только gcc. Теперь я изменил свой код и добавил некоторые новые вещи, как вы можете видеть при редактировании вопроса, PS2, но теперь я получаю разные результаты, комментирующие g ++. Это связано с тем, что я делаю слишком много операций? – flow

0

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

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

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

+0

Да, с -Wall (извините, я полностью забыл), теперь я вижу, когда pragma oh_my_god дает ошибку. С атомной прагмой нет ошибки, но мне интересно, в чем заключается гарантия, что атомная прагма действительно работает, что для меня бесполезно, кажется, бесполезно – flow

0

Вы

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

Так что только распараллеливание будет для цикла.

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

#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

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

Смежные вопросы