Обратите внимание, что omp_get_max_threads()
соответствует максимальному количеству потоков, которое может быть назначено любой команде, выполняющей параллельный регион, но в целом вы не гарантированно получите это количество потоков в данном параллельном регионе. Даже если вы запросите указанное число c потоков с помощью предложения num_threads
для директивы OMP, вы все равно можете получить меньше. Хотя в вашей конкретной программе вы должны получить полное число потоков, это плохая форма, чтобы зависеть от этого.
Вместо этого используйте omp_get_num_threads()
внутри параллельной области, чтобы определить, сколько потоков на самом деле в команде, выполняющей регион. Я также предлагаю использовать omp_get_thread_num()
для получения номера текущего потока в команде, что позволит вам планировать свои итерации l oop вручную, что наиболее удобно, когда алгоритм фактически зависит от того, как они запланированы, как у вас. Кроме того, используйте тот факт, что переменные, объявленные внутри параллельной области, автоматически являются частными по отношению к потокам, выполняющим эту область. В сочетании с объявлением ваших переменных в самой узкой области, это сократит количество необходимых вам разделов данных.
Но все это не решит вашу проблему для меня. То, что делает , разрешает ее (после применения вышеизложенного), перемещая параллельную директиву omp от do
к do
и соответствующему блоку. Это следует интерпретировать как вызов параллельного выполнения блока, а не самого do
. И это не должно быть проблемой для вас, потому что вы все равно хотите барьер в конце каждого выполнения блока. Вам также нужен барьер между вашими двумя внутренними гнездами l oop, чтобы избежать гонок данных.
Объединение всего этого, плюс немного больше реорганизации, дает это, что работает для меня * :
void parallel_bubble_sort(uint64_t *T, const uint64_t size) {
bool swapped;
do {
swapped = false;
#pragma omp parallel
{
register uint64_t swap;
register int i;
int n_threads = omp_get_num_threads();
int thread_num = omp_get_thread_num();
int chunk_size = size / n_threads;
for (i = thread_num * chunk_size + 1;
i < (thread_num + 1) * chunk_size;
i++) {
if (T[i - 1] > T[i]) {
swap = T[i - 1];
T[i - 1] = T[i];
T[i] = swap;
swapped = true;
}
}
#pragma omp barrier
if (i < size && T[i - 1] > T[i]) {
swap = T[i - 1];
T[i - 1] = T[i];
T[i] = swap;
swapped = true;
}
}
} while(swapped);
}
* Он «работает» до (неполной) степени, в которой алгоритм корректен. Алгоритм как написано не является правильным, если только размер массива не кратен числу потоков, выполняющих параллельную область. У моей машины 12 логических ядер (6 физических), а 1024 не кратно 6. Когда я запускаю программу, указанную выше, я получаю несколько конечных элементов, которые не сортируются. Подобное может произойти на любой машине, потому что, опять же, вы не уверены, что получите полное количество ядер по вашему запросу. Исправление этой проблемы оставлено в качестве упражнения.