Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Циклы For и OpenMP


Автор: BrookBond 12.6.2013, 10:05
Приветствую знатоков!

Только начал изучать OpenMP и сразу попытался применить его на практике.
Пишу прогу, которая в дальнейшем будет работать (сравнивать матрицы больших размеров) на многоядерном компе. 
Вот проблемная часть этой проги, где используется OpenMP и возникают непонятные мне действия. 
Код:

Код

#pragma omp parallel for
for(p=0;p<=6300;p++) {

Sp1x=(Sp1.X*Sp1.s+Sp1.R*pow(fi,a/(2*pi))*cos(Sp1.c*a+Sp1.faz))/Sp1.s;//-3*cos(a-faz);
Sp1y=Sp1.y0+Sp1.R*pow(fi,a/(2*pi))*sin(Sp1.c*a+Sp1.faz);//-3*sin(a-faz);
if(a>=0 && a<=2*pi){i1=1;}
if(a>2*pi && a<=4*pi){i1=2;}
if(a>4*pi && a<=6*pi){i1=3;}
if(a>6*pi && a<=8*pi){i1=4;}

    if(Sp1x>XX1+2)  {
     b=0;

     do {
     Sp2x=(Sp2.X*Sp2.s+Sp2.R*pow(fi,b/(2*pi))*cos(Sp2.c*b+Sp2.faz))/Sp2.s;//-3*cos(a-faz);
     Sp2y=Sp2.y0+Sp2.R*pow(fi,b/(2*pi))*sin(Sp2.c*b+Sp2.faz);//-3*sin(a-faz);
     if(b>=0 && b<=2*pi){i2=1;}
     if(b>2*pi && b<=4*pi){i2=2;}
     if(b>4*pi && b<=6*pi){i2=3;}
     if(b>6*pi && b<=8*pi){i2=4;}

         if(Sp2x>XX2+1)  {
     if(Sp1x-0.1<=Sp2x && Sp1x+0.1>=Sp2x && Sp1y-2.5<=Sp2y && Sp1y+2.5>=Sp2y )
                        { //fprintf(f5,"%1.2f \t %1.2f \t%d \t%d\n",Obmen1.x,Obmen1.y,Obmen1.n,Obmen2.n);
                          Obmen3[j].x=Sp1x; Obmen3[j].y=Sp1y; Obmen3[j].n=i1; Obmen3[j].X0=Sp1.X; Obmen3[j].X1=Sp1.X1;
                          Obmen3[j+1].x=Sp2x; Obmen3[j+1].y=Sp2y; Obmen3[j+1].n=i2; Obmen3[j+1].X0=Sp1.X; Obmen3[j+1].X1=Sp1.X1;
        printf("%1.2f \t%1.2f \t%d \t%d \t%d \t%d \n",Obmen3[j].x,Obmen3[j].y,Obmen3[j].n,Obmen3[j].X0,Obmen3[j].X1,p);
// в самом правом столбце выводятся значения внешнего цикла по p                                  
 j=j+2;
                           break;}
                          }
     b+=0.004;
           } while (b<=25.2);
           } //if(Sp1x>Sp1.X1)

a=0.004*p;
}


Проблема в том, что если внешний цикл по р запараллелить, то данные начинают смешиваться, выводятся хаотично так как показано на прикрепленном рисунке (самый правый столбец это значения внешнего цикла по р). А нужно чтобы они выводились последовательно, с начала то что считает первое ядро (0...3150), а затем второе (3151..6300), но чтоб считались параллельно. Подскажите пожалуйста, что добавить/убрать ?
Прикреплен проект в Qt Creator, работаю на вин 7.

Автор: BrookBond 12.6.2013, 10:06
Вот скрин работы проги

Автор: Фантом 12.6.2013, 12:32
Цитата(BrookBond @  12.6.2013,  11:05 Найти цитируемый пост)

Проблема в том, что если внешний цикл по р запараллелить, то данные начинают смешиваться, выводятся хаотично так как показано на прикрепленном рисунке (самый правый столбец это значения внешнего цикла по р). А нужно чтобы они выводились последовательно, с начала то что считает первое ядро (0...3150), а затем второе (3151..6300), но чтоб считались параллельно.

Поскольку Вы используете параллельный цикл, то не можете (и не должны) предсказать, какая из итераций когда будет выполняться. Поэтому, если вывод нужен в строго определенном порядке, то надо сохранять где-то результаты счета, а уже потом в обычном последовательном цикле выполнять вывод результатов. Кстати, и с точки зрения итогового быстродействия это тоже разумнее.

Автор: BrookBond 12.6.2013, 13:02
Спасибо, сейчас как раз над этим работаю

Автор: Wuffur 18.6.2013, 16:14
По-моему надо просто написать:
 
Код

  # pragma omp barrier

Перед printf.

А нет я не прав, надо еще один for запилить.

Автор: BrookBond 18.6.2013, 22:32
Подскажите если знаете, вот такая проблема получается если использовать прагму. Первое ядро в цикле по р считает элементы от 0 до 3150, а второе 3151 - 6300.  Но довольно часто получается, что второе ядро быстрее считает свою часть и в то время как первое ядро считает  i-й элемент (внешний цикл) второе считает уже i+1 элемент и соответственно получается путаница в данных.
Как сделать так чтобы второе ядро ждало завершения счета первого ядра и потом уже они вместе переходили бы на i+1 элемент?  

Код


for(i=0;i<=ab1;i++)//ab1
    {    // printf("%d\n",i);
   
 
#pragma omp parallel for num_threads(2)
for(p=0;p<=6300;p++) {

// кусок кода, здесь идет перебор и сравнение больших матриц

Sp1x=(Sp1.X*Sp1.s+Sp1.R*pow(fi,a/(2*pi))*cos(Sp1.c*a+Sp1.faz))/Sp1.s;//-3*cos(a-faz);
Sp1y=Sp1.y0+Sp1.R*pow(fi,a/(2*pi))*sin(Sp1.c*a+Sp1.faz);//-3*sin(a-faz);
if(a>=0 && a<=2*pi){i1=1;}
if(a>2*pi && a<=4*pi){i1=2;}
if(a>4*pi && a<=6*pi){i1=3;}
if(a>6*pi && a<=8*pi){i1=4;}
}




Автор: Wuffur 18.6.2013, 23:20
Как вариант сначала можно записать в массив, а потом в файл. То есть через указатели.

Ждать пока ядра завершаться:
Код

#pragma omp barrier

я уже сказал.

Мне тут нравится документация:
https://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Fcompiler%2Fref%2Fruompbar.htm

И у меня это код на linux`е segfault`ится.

Вот ещё, вроде можно с :
Код

#pragma omp critical

Так даже в том коде работать должно.

http://stackoverflow.com/questions/10443528/is-there-an-implicit-barrier-after-omp-critical-section
http://msdn.microsoft.com/ru-ru/library/b38674ky.aspx

Автор: BrookBond 19.6.2013, 11:57
Цитата(Wuffur @  18.6.2013,  23:20 Найти цитируемый пост)
код C++
1:
#pragma omp barrier

я уже сказал.

Мне тут нравится документация:
IBM Linux OMP public documentation


Подскажи пожалуйста код, а то никак не могу разобраться с барьером
Код

for(i=0;i<=ab1;i++) {

#pragma omp parallel for
for(p=0;p<=6300;p++) {

Sp1x=(Sp1.X*Sp1.s+Sp1.R*pow(fi,a/(2*pi))*cos(Sp1.c*a+Sp1.faz))/Sp1.s;//-3*cos(a-faz);
Sp1y=Sp1.y0+Sp1.R*pow(fi,a/(2*pi))*sin(Sp1.c*a+Sp1.faz);//-3*sin(a-faz);
if(a>=0 && a<=2*pi){i1=1;}
if(a>2*pi && a<=4*pi){i1=2;}
if(a>4*pi && a<=6*pi){i1=3;}
if(a>6*pi && a<=8*pi){i1=4;}

    if(Sp1x>XX1+2)  {
     b=0;

     do { 

     Sp2x=(Sp2.X*Sp2.s+Sp2.R*pow(fi,b/(2*pi))*cos(Sp2.c*b+Sp2.faz))/Sp2.s;//-3*cos(a-faz);
     Sp2y=Sp2.y0+Sp2.R*pow(fi,b/(2*pi))*sin(Sp2.c*b+Sp2.faz);//-3*sin(a-faz);
     if(b>=0 && b<=2*pi){i2=1;}
     if(b>2*pi && b<=4*pi){i2=2;}
     if(b>4*pi && b<=6*pi){i2=3;}
     if(b>6*pi && b<=8*pi){i2=4;}

         if(Sp2x>XX2+1)  {
     if(Sp1x-0.1<=Sp2x && Sp1x+0.1>=Sp2x && Sp1y-2.5<=Sp2y && Sp1y+2.5>=Sp2y )
                        { //fprintf(f5,"%1.2f \t %1.2f \t%d \t%d\n",Obmen1.x,Obmen1.y,Obmen1.n,Obmen2.n);
            Obmen3[j].x=Sp1x; Obmen3[j].y=Sp1y; Obmen3[j].n=i1; Obmen3[j].X0=Sp1.X; Obmen3[j].X1=Sp1.X1; Obmen3[j].p=p;
            Obmen3[j+1].x=Sp2x; Obmen3[j+1].y=Sp2y; Obmen3[j+1].n=i2; Obmen3[j+1].X0=Sp1.X; Obmen3[j+1].X1=Sp1.X1;Obmen3[j+1].p=p;
        printf("%1.2f \t%1.2f \t%d \t%d \t%d \t%d \n",Obmen3[j].x,Obmen3[j].y,Obmen3[j].n,Obmen3[j].X0,Obmen3[j].X1,Obmen3[j].p);
        printf("%1.2f \t%1.2f \t%d \t%d \t%d \t%d \n",Obmen3[j+1].x,Obmen3[j+1].y,Obmen3[j+1].n,Obmen3[j+1].X0,Obmen3[j+1].X1,p);
                           j=j+2;
                           break;}
                          }
     b+=0.004;
           } while (b<=25.2);
           } //if(Sp1x>Sp1.X1)

if (p==3150 || p==6300) {
 #pragma omp barrier
 }


a=0.004*p;
  

} 

     } // конец цикла по i



При такой записи барьера результата нет, по прежнему идет перескок одного ядра на i+1 элемент, куда его поставить?

Автор: Wuffur 19.6.2013, 13:57
Да , потому что потоки выполняются параллельно до барьера, синхронизируются и дальше выполняются параллельно, поэтому и перескакивает. Если выполняется дериктива #pragma omp critical, то компилятору говорится, что в этом участке кода нужно выполнять в один поток. Вся магия.

То есть если printf запихнуть в 
Код

...
#pragma omp critical {
printf("%1.2f \t%1.2f \t%d \t%d \t%d \t%d \n",Obmen3[j].x,Obmen3[j].y,Obmen3[j].n,Obmen3[j].X0,Obmen3[j].X1,Obmen3[j].p);
        printf("%1.2f \t%1.2f \t%d \t%d \t%d \t%d \n",Obmen3[j+1].x,Obmen3[j+1].y,Obmen3[j+1].n,Obmen3[j+1].X0,Obmen3[j+1].X1,p);
}
...

то всё должно быть нормально.

Что происходит когда ставишь барьерную синхронизацию на 3150 элементе?

Наверное программа распараллеливает for между задачи между ядрами - один выполняет от 1 до 1575 другой от 1576 до 3150, а после от 3151 до 4725 и от 4726 до 6300. 
В таком случае на 6300-ом элементе её ставить нет смысла.

Барьерная синхронизация нужна бы была, если бы у вас элементы в printf() передавались бы переменные из других значений итератора цикла for и у вас выводились еще неинициализированные на каких-то шагах итерации значения переменных. Но это совершенно не влияет на порядок их вывода на стандартный поток.

Без барьерной синхронизации всё не так однозначно,  порядок выполнение параллельных команд может зависеть от состояния кэша и каких-то физических факторов, к примеру температуры процессора.
Вывод сначала в массив был лучше просто вывода в файл или стандартный поток тем, что можно было бы записать произвольно, а в файл(хотя конечно можно и произвольно с fseek, fwrite) и в стандартный поток записывается по порядку.

Автор: BrookBond 19.6.2013, 14:45
Цитата(Wuffur @  19.6.2013,  13:57 Найти цитируемый пост)
Что происходит когда ставишь барьерную синхронизацию на 3150 элементе?


Получается что прога зависает (останавливается).


Цитата(Wuffur @  19.6.2013,  13:57 Найти цитируемый пост)
Наверное программа распараллеливает for между задачи между ядрами - один выполняет от 1 до 1575 другой от 1576 до 3150, а после от 3151 до 4725 и от 4726 до 6300. 


На это не похоже.

попробовал сделать так
Код

#pragma omp critical {
Obmen3[j].x=Sp1x; Obmen3[j].y=Sp1y; Obmen3[j].n=i1; Obmen3[j].X0=Sp1.X; Obmen3[j].X1=Sp1.X1; Obmen3[j].p=p;
            Obmen3[j+1].x=Sp2x; Obmen3[j+1].y=Sp2y; Obmen3[j+1].n=i2; Obmen3[j+1].X0=Sp1.X; Obmen3[j+1].X1=Sp1.X1;Obmen3[j+1].p=p;
        printf("%1.2f \t%1.2f \t%d \t%d \t%d \t%d \n",Obmen3[j].x,Obmen3[j].y,Obmen3[j].n,Obmen3[j].X0,Obmen3[j].X1,Obmen3[j].p);
        printf("%1.2f \t%1.2f \t%d \t%d \t%d \t%d \n",Obmen3[j+1].x,Obmen3[j+1].y,Obmen3[j+1].n,Obmen3[j+1].X0,Obmen3[j+1].X1,p);
                           j=j+2;
}


Становится гораздо лучше, данные выводит почти что верно, но я использую printf лишь для того чтоб отслеживать какие значения записываются в Obmen3 . А при такой записи все равно в Obmen3 записываются не по порядку, а нужно чтобы в этот массив записывались данные в том же порядке как и при работе на одном ядре.   Help.... smile  

Автор: Wuffur 19.6.2013, 15:12
А почему бы вам в массив не писать?
То есть я не совсем понимаю, что там инициализировалось:
Код


struct Obmen1 *Obmen3= new struct Obmen1 [N];


Дебагер вам в руки.

Автор: BrookBond 19.6.2013, 15:14
Цитата(Wuffur @  19.6.2013,  15:12 Найти цитируемый пост)
А почему бы вам в массив не писать? 


Так я же и записываю все данные сравнения в массив структур Obmen3, или что то не так делаю?

Автор: Wuffur 19.6.2013, 15:22
Попробуйте раскомментить второй printf и проверить выполняются ли условия выражения if, если да, то проблема не внутри скобок.

Не знаю, может барьерная синхронизация перед критической секцией поможет, но мне кажется вряд ли.

У меня ваша программа даже под 10-ой студией не запускается.

Автор: Wuffur 19.6.2013, 17:06
Цитата(BrookBond @  19.6.2013,  14:45 Найти цитируемый пост)
Становится гораздо лучше, данные выводит почти что верно, но я использую printf лишь для того чтоб отслеживать какие значения записываются в Obmen3 . А при такой записи все равно в Obmen3 записываются не по порядку, а нужно чтобы в этот массив записывались данные в том же порядке как и при работе на одном ядре.   Help.... smile   

Ну так printf пишет в стандартный поток, сделайте fprintf в критической секции будет писать в файл.

Автор: Wuffur 13.7.2013, 17:28
Попробуйте еще #pragma omp single вначале, чтобы одна нить исполняла одну секцию кода и #pragma omp barrier #pragma omp master перед файловым выводом.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)