在OpenMP中使用原子操作来获取struct(x,y,z)变量

时间:2015-01-28 12:13:58

标签: c++ struct openmp atomic

我正在用C ++开发一个OpenMP代码(编译器是g ++ 4.8.2)。在我的部分代码中,我需要在struct数据上执行原子添加。 strcut定义为:

struct real3 
{
  float x;
  float y;
  float z;
};    

我为它定义了加法运算符如下:

inline real3 operator+(real3 a, real3 b)
{ 
  return make_real3(a.x + b.x, a.y + b.y, a.z + b.z);
}

我在代码的所有部分都使用了这个strcut。在我的程序的一部分中,我需要以原子方式执行添加操作:

real3 * m_cforce;
real3 fn, ft;
int i;
/*
 . . . .  some code is here 

*/

#pragma omp atomic
m_cforce[i] = m_cforce[i] + (fn + ft);

编译不接受struct real3作为原子添加的操作数。一种解决方案是使用以下代码:

#pragma omp atomic
m_cforce[i].x = m_cforce[i].x + (fn + ft).x;
#pragma omp atomic
m_cforce[i].y = m_cforce[i].y + (fn + ft).y;
#pragma omp atomic
m_cforce[i].z = m_cforce[i].z + (fn + ft).z;
以这种方式,我使用原子的3倍以上,这将花费更多的时间。有没有节省方法让我以较低的计算开销执行此操作?

2 个答案:

答案 0 :(得分:0)

OpenMP原子需要在标量值(简单类型)上运行。 Atomics旨在通过运行时映射到内核甚至指令级原子。在不了解您的问题的情况下,很难给出一个答案,但是对于这类事情有一些常见的建议:

  • 使用线程局部变量(如果可能)。如果只有一个线程可以写入它,你可以在大部分计算中避免使用原子,然后在最后减少它们。
  • 上面的3原子方法可以工作,但它允许多个线程添加到可能交错的同一个real3。由于浮点不是关联的,因此可能导致更多非确定性结果。总的来说,这不是一个糟糕的选择。
  • 对每个共享的real3使用OpenMP锁。使用critical就像在包含的代码段周围使用单个锁。如果你对每个real3使用一个锁,只要它们接触到不同的real3,它们就可以并行运行。 OpenMPs锁不是最快的,但它们应该比关键更快。

答案 1 :(得分:0)

@ user2548418:

这是多体模拟,其中所有物体(范围从0到N-1)彼此相互作用。在第一步中,搜索确定成对的交互。然后在所有实体上有一个循环来计算相互作用(这是一个平行部分,其中带有与计数器i的并行)。简单地说,每个主体(i)可以与系统中的0到20个主体(j)相互作用。当我计算相互作用力时,我应该将这些力加到每个物体的总力​​量上(i和j) 我上面演示的代码是执行此添加的部分,因此如果您希望此部分完整,请考虑以下内容:

#pragma omp atomic
m_cforce[i].x = m_cforce[i].x + (fn + ft).x;
#pragma omp atomic
m_cforce[i].y = m_cforce[i].y + (fn + ft).y;
#pragma omp atomic
m_cforce[i].z = m_cforce[i].z + (fn + ft).z;

#pragma omp atomic
m_cforce[j].x = m_cforce[j].x - (fn + ft).x;
#pragma omp atomic
m_cforce[j].y = m_cforce[j].y - (fn + ft).y;
#pragma omp atomic
m_cforce[j].z = m_cforce[j].z - (fn + ft).z;

由于每个线程将修改两个不同的存储器位置:一个在i位置,其对应于迭代计数器,一个在j位置,其是随机的,并且可以是范围从0到N-1的任何数字。这种依赖关系不允许我考虑每个线程的私有标量real3。有一种解决方案。我为每个线程分配了一个real3 [N]的向量,然后在"并行的末尾使用了一个减少和为"找到每个身体的总力量。这可能具有挑战性,因为它可以降低捕获效率(原子也会这样做)。所以,我应该将此解决方案与使用原子进行比较。

我知道浮点运算不是关联的。但是在我的情况下它并不是限制性的,因为力的顺序并没有很大差异,当操作a +(b + c)变为(a + b)+ c时,误差会非常小。

我不打算在我的代码中使用锁,否则我的第一个和第二个解决方案的性能会非常糟糕。