Я не знаю, что не так с этим параллелизмом цикла - PullRequest
0 голосов
/ 13 ноября 2018

Я не знаю почему, но распараллеливание этого цикла не работает нормально:

#pragma omp parallel for private(d) 
for ( i = 0 ; i < nt1 ; i++ ) {
  d = distancia(tabla1[i],tabla2[j]);
  if ( d < dm ) {
    #pragma omp critical
    if ( d < dm ) {
      dm = d;
      im = i;
    }
  }
}
ps[j] = im;

Переменная d является закрытой, а dm и im находятся внутри критической зоны, но когда я выполняю этот код (полный код больше, это лишь малая его часть), выходной сигнал меняется с последовательная версия. Спасибо!

Ответы [ 2 ]

0 голосов
/ 17 ноября 2018

Я полагаю, что проблема заключалась в том, что даже если это была критическая зона, в последовательной версии мы всегда получим самое низкое значение i и добавим следующее условие: (dm == d && i < im) в условных выражениях мы гарантируем, что код все еще работает таким же образом. Я не уверен, что это лучшее решение, но оно довольно простое и работает для меня. Вот полный код с изменениями:

for ( j = 0 ; j < nt2 ; j++ ) {
  dm = MAX_LON + 1;
  #pragma omp parallel for private(d) 
  for ( i = 0 ; i < nt1 ; i++ ) {
    d = distancia(tabla1[i],tabla2[j]);
    if ( d < dm  || (dm == d && i < im)) {
      #pragma omp critical
        {
        if ( d < dm  || (dm == d && i < im)) {
          dm = d;
          im = i;
        }
        }
    }
  }
  ps[j] = im;
}
0 голосов
/ 14 ноября 2018

Первый if ( d < dm ) находится за пределами критической секции 1003 * и может быть причиной состояния гонки.Его следует удалить, поскольку в разделе критический он уже есть.Если ваша цель состоит в том, чтобы не заходить слишком часто в критическую часть, то это не способ сделать это (см. Ниже).Возможно, вам придется вручную сбросить dm (см. здесь для некоторых пояснений по #pragma omp flush).

Функция distancia также может быть не поточно-ориентированной (то есть полагаться на некоторые глобальныепеременная, которая не может быть одновременно отредактирована несколькими потоками).

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

Если вычисление функции distancia не требует интенсивной загрузки ЦП, ваш код, вероятно, будет работать медленнее, чем последовательный код по многим причинам.Примечательно, что накладные расходы синхронизации критической секции и проблема совместного использования кэша с переменной dm.Лучше использовать подход сокращения, где каждый поток вычисляет минимум любой итерации, которую он дал обработке, и глобальный минимум принимается как наименьший локальный минимум из всех потоков.

float dm=INFINITY;
int im=-1;

#pragma omp parallel shared(dm,im)
{
  // Declare private variables
  float d,dmin_thread,imin_thread;
  dmin_thread=INFINITY;
  imin_thread=-1;

  // loop through i
  #pragma omp for
  for ( i = 0 ; i < nt1 ; i++ ) {
    d = some_function(i);
    // the following lines operate on thread-private variables
    if ( d < dmin_thread) {
      dmin_thread= d;
      imin_thread= i;
      }
    }// end for

  // Reduction part after the loop has been completed
  // The critical section is entered only once by each thread
  #pragma omp critical
  {
    if ( dmin_thread < dm) {
      dm = dmin_thread;
      im = imin_thread;
      }
  }//end critical

}//end parallel

if(im==-1)
  throw_some_error();
else
  do_something_with(im);
...