довольно мало, каждый результат обновляется атомарно. Самое главное, вы не хотите читать счетчик в критическом разделе.

я есть код, который выполняет много итераций, и только если условие выполняется, результат итерации сохраняется. Это естественно выражается как цикл while. Я пытаюсь заставить код работать параллельно, так как каждая реализация независима. Итак, у меня есть это:

while(nit<avit){
    #pragma omp parallel shared(nit,avit)
    {
        //do some stuff
        if(condition){
            #pragma omp critical
            {
                nit++;
                \\save results
            }
        }
    }//implicit barrier here
}

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

Есть ли способ избежать этого барьера, чтобы потоки продолжали работать? Я в среднем тысячи итераций, так что еще несколько не больно (в случае, еслиnit переменная не была увеличена в уже запущенных потоках) ...

Я пытался превратить это в параллель для, но автоматическое увеличение цикла for делаетnit переменная сходит с ума. Это моя попытка:

#pragma omp parallel shared(nit,avit)
{
    #pragma omp for
    for(nit=0;nit<avit;nit++){
        //do some stuff
        if(condition){
            \\save results
        } else {
            #pragma omp critical
            {
                nit--;
            }
        }
    }
}

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

Я также пытался оставить приращение в цикле for пустым, но он не компилируется, или пытался обмануть мой код, чтобы не иметь приращения в цикле for, например

...
incr=0;
for(nit=0;nit<avit;nit+=incr)
...

но тогда мой код вылетает ...

Есть идеи?

Спасибо

Изменить: Вот рабочий минимальный пример кода в цикле while:

#include <random>
#include <vector>
#include <iostream>
#include <time.h>
#include <omp.h>
#include <stdlib.h>
#include <unistd.h>

using namespace std;

int main(){

    int nit,dit,avit=100,t,j,tmax=100,jmax=10;
    vector<double> Res(10),avRes(10);

    nit=0; dit=0;
    while(nit<avit){
        #pragma omp parallel shared(tmax,nit,jmax,avRes,avit,dit) private(t,j) firstprivate(Res)
        {
            srand(int(time(NULL)) ^ omp_get_thread_num());
            t=0; j=0;
            while(t<tmax&&j<jmax){
                Res[j]=rand() % 10;
                t+=Res[j];
                if(omp_get_thread_num()==5){
                    usleep(100000);
                }
                j++;
            }
            if(t<tmax){
                #pragma omp critical
                {
                    nit++;
                    for(j=0;j<jmax;j++){
                        avRes[j]+=Res[j];
                    }
                    for(j=0;j<jmax;j++){
                        cout<<avRes[j]/nit<<"\t";
                    }
                    cout<<" \t nit="<<nit<<"\t thread: "<<omp_get_thread_num();
                    cout<<endl;
                }
            } else{
                #pragma omp critical
                {
                    dit++;
                    cout<<"Discarded: "<<dit<<"\r"<<flush;
                }
            }
        }
    }
    return 0;
}

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

 Zulan18 нояб. 2017 г., 11:13
На самом деле невозможно правильно ответить на вопрос без более конкретного пониманиянекоторые вещи, В частности, мы понятия не имеем,nit доступ внекоторые вещи и что должно произойти, когда несколько потоков имеютcondition в то же время, илиnit был обновлен несколько раз, пока один поток делалнекоторые вещи... Это сложно, но для хорошего, конкретного ответа вы должны создатьМинимальный, полный и проверяемый пример.
 Francisco Herrerias-Azcue21 нояб. 2017 г., 12:12
Спасибо за комментарий @Zulan. Я отредактировал вопрос, чтобы добавить минимальный рабочий пример в конце. Надеюсь, это проясняет ситуацию.

Ответы на вопрос(1)

Решение Вопроса

Вы можете в основном следовать той же концепции, что и дляэтот вопросс небольшим изменением, чтобы гарантировать, чтоavRes не записывается параллельно:

int nit = 0;
#pragma omp parallel
while(1) {
     int local_nit;
     #pragma omp atomic read
     local_nit = nit;
     if (local_nit >= avit) {
          break;
     }

     [...]

     if (...) { 
          #pragma omp critical
          {
                #pragma omp atomic capture
                local_nit = ++nit;
                for(j=0;j<jmax;j++){
                    avRes[j] += Res[j];
                } 
                for(j=0;j<jmax;j++){
                    // technically you could also use `nit` directly since
                    // now `nit` is only modified within this critical section
                    cout<<avRes[j]/local_nit<<"\t";
                }
          }
     } else {
          #pragma omp atomic update
          dit++;
     }
 }

Это также работает с критическими областями, но атомы более эффективны.

Есть еще одна вещь, которую вы должны рассмотреть,rand() не следует использовать в параллельных контекстах. Видетьэтот вопрос, Для C ++ используйте частный (то есть определенный в параллельной области) генератор случайных чисел из<random>.

 Francisco Herrerias-Azcue24 нояб. 2017 г., 09:48
Это имеет немного больше смысла ... но это снова будет писать только в конце программы, верно? Мне нужно иметь возможность чаще проверять результаты, поэтому я думаю, что мне нужен критический раздел ... Могу ли я "скрыть это" вif?:#pragma omp atomic capture local_wnit=++wnit; if(local_wnit==wavit){ #pragma omp critical { \\write; wnit-=local_wnit;} } гдеwnit это глобальный счетчик записиlocal_wnit локально обработанная переменная иwavit общая переменная с количеством итераций между событиями записи. Это поможет или это то же самое? Спасибо за исправление ссылки.
 Zulan23 нояб. 2017 г., 14:42
Мой код ошибочно использовал постфикс вместо приращения префикса. Я исправил это и ссылку. С правильной формойlocal_nit = ++nit; if (local_nit == avit) ... Вы можете защитить запись вывода без дорогостоящего критического раздела.
 Francisco Herrerias-Azcue23 нояб. 2017 г., 09:43
Спасибо @Zulan, с небольшими изменениями это, кажется, (почти) делает свое дело! Я не очень знаком сatomic, но это не позволило бы мне добавить блок кода, как сcritical... Мне нужно, чтобы все потоки могли писать результаты, и чтобы они писались чаще, чем просто в конце ... Если я это сделаю#pragma omp critical { local_nit = nit++; for(j=0;j<jmax;j++){ avRes[j]+=Res[j]; } [...] } это работает, но мне любопытно, как я мог бы использоватьatomic, так как вы упомянули, является более эффективным. Также ссылка на первый вопрос кажется неправильной ...
 Zulan24 нояб. 2017 г., 11:00
Если вам нужно обновлять результат для каждого приращения (извините, я неправильно прочитал ваш вопрос), вы также можете просто использовать критический раздел для обновления. Смотрите мой отредактированный ответ. В качестве альтернативы вы можете сделать сокращение, если просто подвести итог, если производительность является проблемой с критическим разделом - или еслиjmax довольно мало, каждый результат обновляется атомарно. Самое главное, вы не хотите читать счетчик в критическом разделе.
 Francisco Herrerias-Azcue23 нояб. 2017 г., 11:28
Неважно, теперь я понимаю, чтоatomic может использоваться только для однострочных инструкций. Таким образом, окончательный код должен иметьcritical раздел для письма. И, как окончательное исправление, я считаю,local_nit = nit++; должно быть простоnit++;, посколькуlocal_nit обновлено выше, в#pragma omp atomic read local_nit = nit; иif что мешает писать до конца не хочется ... Еще раз спасибо за помощь.

Ваш ответ на вопрос