OpenMP для цикла не дает правильный результат - PullRequest
0 голосов
/ 09 декабря 2011

Извините, я только начал изучать OpenMP, поэтому я немного растерялся.Я анализирую моё моделирование молекулярной динамики и в одной части кода пытаюсь найти ближайшее расстояние между молекулами воды (или ионами) и белком.Это очень трудоемкая часть, потому что у меня около 500000 атомов и около 25000 кадров.Для одного процессора это занимает 1 неделю (для набора вычислений не только расстояние).

Я изменил эту часть кода на параллельную с помощью OpenMP, и это действительно быстро, но с небольшой ошибкой;90% результатов (расстояния) верны, а 10% неверны, по сравнению с кодом одного процессора.Это часть моего кода, которая вычисляет ближайшее расстояние:

    ...
    for (i=0; i< number of frames(25000) )
    ...
    // XP,YP,ZP protein coordinates; malloc allocation in the code
    // XI,YI,ZI Sodium molecule coordinates; malloc allocation
    // LX,LY,LZ the dimension of simulation box, malloc allocation
    // dimI defined as a temporary closest distance, filled with very large constant,
    // malloc allocation
    // NSOD number of Sodium molecules
    // rhos keeping the closest distance for each Sodium for each frame.
    …
    ...int l=0,kk=0;
#pragma omp parallel for shared(XI,YI,ZI,XP,YP,ZP,LX,LY,LZ,qq,dimI,distI,rhos,xmin,ymin,zmin,i) private(kk,l)
      for (l=0; l < NSOD; l++){
        // this part relocates every thing inside a box with dimension LX*LY*LZ. xmin, ymin and zmin are the boundaries of the box.
        if (XI[l]!=0.0 || YI[l]!=0.0 || ZI[l]!=0.0){
          if (XI[l] < xmin) XI[l] += ceil((xmin - XI[l])/LX[i-1]) * LX[i-1];
          if (XI[l] > xmax) XI[l] -= ceil((XI[l] - xmax)/LX[i-1]) * LX[i-1];
          if (YI[l] < ymin) YI[l] += ceil((ymin - YI[l])/LY[i-1]) * LY[i-1];
          if (YI[l] > ymax) YI[l] -= ceil((YI[l] - ymax)/LY[i-1]) * LY[i-1];
          if (ZI[l] < zmin) ZI[l] += ceil((zmin - ZI[l])/LZ[i-1]) * LZ[i-1];
          if (ZI[l] > zmax) ZI[l] -= ceil((ZI[l] - zmax)/LZ[i-1]) * LZ[i-1];
        }
        for (kk=0; kk<NP; kk++){

         if ( ( XP[kk]!=0. || YP[kk]!=0. || ZP[kk]!=0. )  ){
           distI[l] = sqrt((XI[l]-XP[kk])*(XI[l]-XP[kk]) + (YI[l]-YP[kk])*(YI[l]-YP[kk]) + (ZI[l]-ZP[kk])*(ZI[l]-ZP[kk]) );
           if (distI[l] < dimI[l] ) {
              dimI[l] = distI[l];
           }
         }
        }
        distI[l] = dimI[l];
        rhos[qq][l] = dimI[l];

} #pragma omp барьер ...

Не могли бы вы сказать, что не так с моим кодом после распараллеливания?Почему только в некоторых случаях это дает неправильный ответ, а не во всех случаях?Я высоко ценю ваши комментарии и рекомендации.Я использую GCC на Linux.Большое спасибо,

Приветствия, Араш

Ответы [ 3 ]

2 голосов
/ 09 декабря 2011

При работе с плавающими точками может быть не очень хорошей идеей иметь

if (XI[l]!=0.0 || YI[l]!=0.0 || ZI[l]!=0.0){

, вместо этого вам следует сравнить с epsilon (это очень небольшое число)в противном случае возникает проблема.

1 голос
/ 09 декабря 2011

В дополнение к ответу Андерса.

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

Цифры - это искусство.

0 голосов
/ 10 декабря 2011

Моя проблема была решена случайно, хотя я ненавижу этот вид слепого решения проблемы;поэтому я не могу найти никакого объяснения этому.Как я уже упоминал, я должен найти самое близкое расстояние между белком-водой, белком-натрием и белком-хлоридом.Для этой работы мне нужна временная переменная с плавающей запятой, чтобы сравнить новое расстояние с предыдущим и, если оно меньше, заменить минимальное расстояние.Я подумал, что, поскольку я собираюсь запустить это в разных потоках, поэтому безопаснее определить одномерный массив с плавающей запятой для этого сравнения в моем коде.Я определил одномерный массив с плавающей точкой dimI[l] и заполнил его очень большим числом с плавающей точкой во время инициализации.

Я попробовал zillion комбинацию общих и приватных переменных в команде openmp в своем коде, и, наконец, я заменил этот 1D-массив на обычный float и определил его как приватную переменную;Удивительно, но это решило проблему, и теперь оно работает отлично.

Почему одномерный массив не работает как временная переменная?Кажется, что безопаснее определить одномерный массив;независимо от количества потоков, следующим образом, где kk=0..NSOD является приватным, а все остальные являются общими переменными

  Thread 0            T1                  T2                      
  l=0 ..10         l=11..20            l=21..30               
kk1=0..NSOD       kk2=0..NSOD         kk3=0..NSOD  
XI[l],XP[kk1]    XI[l] ,XP[kk2]      XI[l],XP[kk3]
  distI[l]          distI[l]            distI[l]
  dimI[l]           dimI[l]             dimI[l]

Что не так с вышеуказанным методом?

Cheers, Arash

...