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 x = 1.0f; // ... } } delete[]a;
我应该将local_a
私有和私人私人? 我实际上是OpenMP和C的新手。
这取决于你想对数组做什么。 每个线程是否应该访问相同的数组,而不需要将指针设置为threadprivate。
在你的情况下,如果你设置a
私有,无关紧要,因为指针只将地址存储到内存中。 如果所有线程使用相同的变量来访问相同的地址,或者如果每个线程都有自己的相同地址的副本,则没有区别。
如果每个线程都应该有自己的数组副本,那么您需要分别为每个线程分配和删除内存。 如果您的编译器支持为线程私有对象调用已创建的C ++对象的正确构造函数,则可以在您的情况下使用std :: vector。
std::vector a; #pragma omp parallel for firstprivate(a) for (j = 0; j < 10; j++) { A * local_j = &a[j]; local_j->x = 1.0f; // ... }
否则,您需要为每个线程分配一个单独的内存块。
const int num_threads = omp_get_num_threads(); A** a; a = new A*[num_threads]; for (int i=0; ix = 1.0f; // ... } for (int i=0; i
重要的是要知道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
请注意,每个线程的地址都相同。 现在让我们尝试传递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
现在每个线程都有其唯一的私有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
现在唯一的区别是复制a
ARE的值。
动态数组:
int *a = new int[10]; for(int i=0; i<10; i++) a[i]=i;
让我们先看看传递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
每个线程都具有相同的静态数组。 当我们使用私有时,会发生差异。
#pragma omp parallel private(a) //segmentation fault
每个线程都有自己的私有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
现在我们看到每个线程都有自己的私有a
,但是,与静态数组不同,每个线程仍然指向SAME内存地址。 所以指针是私有的,但它们指向的地址是相同的。 这实际上意味着内存仍然是共享的。
如何分配私有版本的动态数组
要为每个线程获取私有版本的动态数组,我不建议将它们分配到并行区域之外。 原因是,如果你不小心,很容易造成虚假分享。 请参阅此问题/答案关于通过在并行区域外部分配内存而导致的错误共享OpenMP实现的减少 。 您可以使用双指针,但这不一定能解决错误共享问题,并且不会解决多插槽系统上的其他问题。 在这些系统上,套接字不共享同一页面(或者您获得另一种虚假共享)非常重要。 如果让每个线程分配其内存,您不必担心这一点。
一般来说,我会为每个线程分配一个数组的私有版本,然后将它们合并到一个关键部分。 但是,有些情况只能分配一次,但是在不使用临界区的情况下,与OpenMP并行填充直方图(数组缩减)是很复杂的。