Новости
О Центре
Кластер
Обучение
Основной курс по параллельному программированию
Учебные курсы
Магистратура
Дополнительное образование
Работы студентов
Библиотека
Исследования
Конференции
Полезные ссылки
NVIDIA
Контакты
О сайте
Имя:
Пароль:
запомнить:
Забыли пароль? Регистрация

Параллельная реализация

В алгоритме необходимо выделить части, обладающие наибольшей вычислительной нагрузкой и подходящие для распараллеливания. Для этого следует обратить внимание на операции, никак не зависящие от данных и  результатов друг друга. Из алгоритма видно, что результат слияния одной пары упорядоченных частей никак не зависит от результата упорядочивания другой на данной итерации. Именно эту часть мы и будем распараллеливать. Однако распараллеливание внутреннего цикла распределения частей слияния тоже не даст особого результата, так как потоки будут часто получать малые по размеру задачи, в результате чего накладные расходы на открытие и закрытие потоков будут велики. Распараллеливать саму функцию слияния не имеет смысла, так как в случае разбиения сливаемых массивов на несколько частей мы лишь сымитируем действия предыдущей итерации алгоритма. Таким образом, мы разделим весь массив на число потоков, и для каждой части выполним последовательную сортировку на отдельном потоке.

inline void Merge (Element *M, int l1, int l2, int k1, int k2, Element *Buff, int N) {

   int i1, i2, i;

   i1 = l1;

   i2 = k1;

 

   i = l1;

 

   while ( i <= k2) {

         if (M[i1] > M[i2]) {

           Buff[i] = M[i2];

             i2++;

             i++;

         }

         else {

          Buff[i] = M[i1];

             i1++;

             i++;

         }

 

         if ((i1 > l2)&&(i2 <= k2)) {

               while (i2 <= k2) {

         Buff[i] = M[i2];

            i2++;

            i++;

               }

         }

 

         if ((i1 <= l2)&&(i2 > k2)) {

               while (i1 <= l2) {

         Buff[i] = M[i1];

            i1++;

            i++;

               }

         }

 

   

    }

   for (i = l1, i1 = l1; i < k2+1; i++, i1++ ) {

     M[i] = Buff[i1];

   }

}

 

void Sort (Element *M, int N1, int N2, Element *Buff) {

int i, Nk, step, K, K2, temp;

int N = N2 - N1 +1;

 

      Nk = ((int)(N/2))*2;

 

      for (i = N1; i < N1+Nk; i+=2) {

            if (M[i] > M[i+1]) {

 

           M[i] = M[i] + M[i+1];

               M[i+1] = M[i] - M[i+1];

               M[i] = M[i] - M[i+1];

            }

      }

 

 

 

      for (step = 2; step < N ; step = step*2) {

            K = (int)(N/step);

            K2 = ((int)(K/2))*2;

            for (i = N1; i < N1+K2*step; i+=step*2) {

                 

         Merge (M, i, i+step - 1, i+step , i+step*2 -1, Buff, N);

            }

 

            if (((N - K*step) > 0)&&((K - K2) > 0)) {

      Merge (M, K2*step+N1, (K2+1)*step - 1 +N1, (K2+1)*step +N1, N2, Buff, N);

            }

      }      

}

 

void SortParallel (Element *M, int N, int NUM_THREADS) {

  Element *Buff = new Element[N];

      int k = (int)((N-1)/2);

      #pragma omp parallel sections

      {

#pragma omp section

            Sort(M, 0, k, Buff);

#pragma omp section

            Sort (M, k+1, N-1, Buff);

      }

     

      Merge (M, 0, k, k+1, N-1, Buff, N);

      delete []Buff;

}

Последовательная функция Merge использует буфер,  выделяемый один раз в функции SortParallel, размером в заданный массив.  Последовательная функция сортировки Sort используется два раза в функции SortParallel, рассчитанной на два потока. Большее количество потоков приведёт к тому, что после работы всех потоков над своими частями, придётся эти части сливать либо в основном потоке, любо снова распределять между потоками, что приведёт к существенным накладным расходам.

Новости

22.10.2012
04.09.2012
05.04.2012
06.03.2012
02.03.2012