OpenMP中的本地指针

Gea*_*ric 1 c c++ openmp

局部变量应该对每个线程自动私有.如何将本地指针指向并行区域外的某个地址,例如

A * a = new A[10];
int i, j;

for (i = 0; i < 10; i++){
    A * local_i = &a[i];
    // do sth ...
    #pragma omp parallel for
    for (j = 0; j < 10; j++){
        A * local_j = &a[j];
        local_j->x = 1.0f;
        // ...
    }
}
delete[]a;
Run Code Online (Sandbox Code Playgroud)

我应该local_a私人和afisrtprivate吗?我实际上是OpenMP和C的新手.

Z b*_*son 8

重要的是要知道OpenMP以不同方式处理静态和动态数组.在示例中,您提供的静态数组更合适.让我们来看看在静态和动态数组上使用shared,private和firstprivate时会发生什么.我将为每种情况打印线程号,a的地址,a的值和数组的值.

静态数组:

int a[10];
for(int i=0; i<10; i++) a[i]=i;

#pragma omp parallel
{
    #pragma omp critical
    {
        printf("ithread %d %p %p :", omp_get_thread_num(), &a, a); for(int i=0; i<10; i++) printf("%d ", a[i]); printf("\n");             
    }
}
//ithread 1 0x7fff3f43f9b0 0x7fff3f43f9b0 :0 1 2 3 4 5 6 7 8 9 
//ithread 3 0x7fff3f43f9b0 0x7fff3f43f9b0 :0 1 2 3 4 5 6 7 8 9 
Run Code Online (Sandbox Code Playgroud)

请注意,每个线程都具有相同的地址a.现在让我们尝试传递a私人.

#pragma omp parallel private(a)
//ithread 0 0x7fffc7897d60 0x7fffc7897d60 :4 0 -1393351936 2041147031 4 0 0 0 4196216 0 
//ithread 1 0x7fa65f275df0 0x7fa65f275df0 :0 0 0 0 0 0 1612169760 32678 1596418496 32678 
Run Code Online (Sandbox Code Playgroud)

现在每个线程都有它的私有a,每个私有版本都指向不同的内存地址.但是,它们的数组值未被复制.现在让我们试试吧firstprivate(a)

#pragma omp parallel firstprivate(a)
//ithread 0 0x7ffffb5ba860 0x7ffffb5ba860 :0 1 2 3 4 5 6 7 8 9 
//ithread 3 0x7f50a8272df0 0x7f50a8272df0 :0 1 2 3 4 5 6 7 8 9 
Run Code Online (Sandbox Code Playgroud)

现在唯一的区别a是复制了ARE 的值.

动态数组:

int *a = new int[10];
for(int i=0; i<10; i++) a[i]=i;
Run Code Online (Sandbox Code Playgroud)

让我们首先看一下a共享的传递

#pragma omp parallel
//ithread 2 0x7fff86a02cc8 0x9ff010 :0 1 2 3 4 5 6 7 8 9 
//ithread 0 0x7fff86a02cc8 0x9ff010 :0 1 2 3 4 5 6 7 8 9
Run Code Online (Sandbox Code Playgroud)

每个线程都a像静态数组一样.当我们使用私有时,会发生差异.

#pragma omp parallel private(a)
//segmentation fault
Run Code Online (Sandbox Code Playgroud)

每个线程都有自己的私有a,就像静态数组一样,但每个版本指向的内存地址是未分配的随机内存.当我们尝试阅读它时,我们会遇到分段错误.我们可以解决这个问题firstprivate(a)

 #pragma omp parallel firstprivate(a)
 //ithread 0 0x7fff2baa2b48 0x8bd010 :0 1 2 3 4 5 6 7 8 9 
 //ithread 1 0x7f3031fc5e28 0x8bd010 :0 1 2 3 4 5 6 7 8 9
Run Code Online (Sandbox Code Playgroud)

现在我们看到每个线程都有自己的私有a,但是,与静态数组不同,每个线程仍然指向SAME内存地址.因此指针是私有的,但它们指向的地址是相同的.这实际上意味着内存仍然是共享的.

如何分配私有版本的动态数组

要为每个线程获取私有版本的动态数组,我不建议将它们分配到并行区域之外.原因是,如果你不小心,很容易造成虚假分享.请参阅此问题/答案关于通过在并行区域外部分配内存而导致的错误共享OpenMP实现的减少.您可以使用双指针,但这不一定能解决错误共享问题,并且不会解决多插槽系统上的其他问题.在这些系统上,套接字不共享同一页面(或者您获得另一种虚假共享)非常重要.如果让每个线程分配其内存,您不必担心这一点.

通常,我会为每个线程分配一个数组的私有版本,然后将它们合并到一个关键部分.但是,有些情况只能分配一次,但是在没有使用临界区的情况下,与OpenMP并行填充直方图(数组缩减)是很复杂的.