C 并行冒泡排序被阻止

C 并行冒泡排序被阻止,c,algorithm,sorting,parallel-processing,openmp,C,Algorithm,Sorting,Parallel Processing,Openmp,我的OpenMP程序在下面代码的第一个“for”循环中阻塞,没有任何明显的原因。 我只是想把冒泡排序并行化 下面是复制该问题的完整代码: #include <stdint.h> #include <stdbool.h> #include <stdlib.h> #include <omp.h> static int N_THREADS; #define CHUNK_SIZE (size/N_THREADS) void parallel_bubbl

我的OpenMP程序在下面代码的第一个“for”循环中阻塞,没有任何明显的原因。 我只是想把冒泡排序并行化

下面是复制该问题的完整代码:

#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <omp.h>

static int N_THREADS;
#define CHUNK_SIZE (size/N_THREADS)

void
parallel_bubble_sort(uint64_t *T, const uint64_t size)
{
    register bool swapped;
    register uint64_t swap;
    register int i,j;

    #pragma omp parallel private(swap,i,j)
    do {
        swapped = false;

        #pragma omp for schedule(static) reduction(||:swapped)
        for (j=0; j<N_THREADS; j++)
        for (i=j*CHUNK_SIZE+1; i<=(j+1)*CHUNK_SIZE-1; i++)
        if (T[i-1] > T[i]) {
            swap = T[i-1];
            T[i-1] = T[i];
            T[i] = swap;
            swapped = true;
        }

        #pragma omp for schedule(static) reduction(||:swapped)
        for (i=CHUNK_SIZE-1; i<size-CHUNK_SIZE; i+=CHUNK_SIZE)
        if (T[i] > T[i+1]) {
            swap = T[i];
            T[i] = T[i+1];
            T[i+1] = swap;
            swapped = true;
        }
    } while(swapped);
}

int main ()
{
    uint64_t i;
    uint64_t N = 1024;
    N_THREADS = omp_get_max_threads();

    uint64_t *X = (uint64_t *) malloc(N * sizeof(uint64_t));
    for (i = 0 ; i < N ; i++) X[i] = N-i;

    parallel_bubble_sort(X, N);
    free(X);
}
#包括
#包括
#包括
#包括
静态int N_线程;
#定义块大小(大小/N个线程)
无效的
并行气泡排序(uint64\u t*t,常量uint64\u t大小)
{
寄存器布尔交换;
寄存器uint64\u t交换;
寄存器int i,j;
#pragma omp并行专用(交换,i,j)
做{
交换=假;
#计划(静态)缩减的pragma omp(| |:交换)

对于(j=0;j而言,死锁的原因是最外层循环中的数据争用情况:

do {
   swapped = false;  // <--- here
   ...
} while(swapped);    // <--- here
另外,让所有线程重置
交换
也不是真正必要的,它可能(不确定)违反OpenMP规范,该规范禁止在缩减完成之前并发访问原始变量。我不确定它是否适用于缩减区域之前的修改(因为我不确定)OpenMP 4.5规范中删除了一段关于并发访问的内容,但为了安全起见,我将对助理进行
单一
处理:

do {
   #pragma omp barrier
   #pragma omp single
   swapped = false;
   ...
} while(swapped);

请注意,
omp\u get\u max\u threads()
计算为可能分配给执行并行区域的任何团队的最大线程数,但通常情况下,不能保证在给定的并行区域中获得该数量的线程。即使通过OMP指令的
num_threads
子句请求特定数量的线程,也可能会得到较少的.Al虽然在您的特定程序中,您应该获得完整数量的线程,但依赖于它是很糟糕的

相反,在并行区域内使用
omp\u get\u num\u threads()
来确定执行该区域的团队中实际有多少线程。我建议也使用
omp\u get\u thread\u num()
获取团队中当前线程的编号,这将允许您手动安排循环迭代,当算法实际取决于如何安排循环迭代时,这是最合适的,正如您所做的那样。此外,请务必利用并行区域内声明的变量对于执行该区域的线程。再加上在最窄的范围内声明变量,这将减少所需的数据共享子句的数量

但这些都不能解决我的问题。在应用上述方法后,解决问题的方法是什么正在将omp parallel指令从
do
之前移动到
do
与其关联的块之间。这应解释为要求并行执行块,而不是
do
本身。这对您来说应该不是问题,因为您希望在每次执行无论如何都要阻塞。您还需要在两个内部循环嵌套之间设置屏障,以避免数据争用

把所有这些放在一起,再加上一点重组,就会产生这样的结果,这对我来说是可行的:

void并行气泡排序(uint64\u t*t,const uint64\u t size){
布尔交换;
做{
交换=假;
#pragma-omp并行
{
寄存器uint64\u t交换;
寄存器int i;
int n_threads=omp_get_num_threads();
int thread_num=omp_get_thread_num();
int chunk_size=大小/n_线程;
对于(i=线程数*块大小+1;
i<(线程数+1)*块大小;
(i++){
if(T[i-1]>T[i]){
swap=T[i-1];
T[i-1]=T[i];
T[i]=互换;
交换=真;
}
}
#布拉格奥姆普屏障
如果(iT[i]){
swap=T[i-1];
T[i-1]=T[i];
T[i]=互换;
交换=真;
}
}
}while(交换);
}


*它“工作”到算法正确的程度(不完整)。除非数组大小是执行并行区域的线程数的倍数,否则所写的算法是不正确的。我的机器有12个逻辑核(6个物理核),1024不是6的倍数。当我运行上面的程序时,我会得到一些未排序的尾随元素。任何机器上都可能发生类似的情况,因为您通常不确定是否能获得所请求的全部内核数。解决该问题只是一个练习。

谢谢您的回答。事实上,我会循环查找j@JePierreCoffe你有一个完整的完整的功能代码来重现问题吗?如果你删除
寄存器
关键字会发生什么?还有,你使用什么操作系统和编译器?谢谢你的耐心。我用一个完整的例子编辑了我的原始文章,重现了问题。删除寄存器关键字不会改变问题解决这个问题。我使用Debian Stretch、gcc和我在O0模式下编译。@Jean PierreCoffe好的,我尝试了你的代码,它被阻止了,在一些调试后发现了一个从一开始就应该很明显的问题-存在数据竞争。相应地更新了我的答案。Ca!C'est de la merde!RIP JPC如果你移动
I并将
交换到
j
for循环中(以便在每个线程上声明它们,而不是依赖于private)?不能仅在
j
for循环中声明@1201programalm
swapped
,因为它被do while循环使用。@JeanPierreCoffe
swapped
不受保护,由多个并行线程设置为
false
,没有任何障碍。因此,当其他线程执行时,可以将其设置为
false
g
j
循环,这可能会导致竞争条件。哎呀-我的意思是
swap
。谢谢你的回答。Movin
do {
   #pragma omp barrier
   #pragma omp single
   swapped = false;
   ...
} while(swapped);
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);
}