我正在用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倍以上,这将花费更多的时间。有没有节省方法让我以较低的计算开销执行此操作?
答案 0 :(得分:0)
OpenMP原子需要在标量值(简单类型)上运行。 Atomics旨在通过运行时映射到内核甚至指令级原子。在不了解您的问题的情况下,很难给出一个答案,但是对于这类事情有一些常见的建议:
答案 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时,误差会非常小。
我不打算在我的代码中使用锁,否则我的第一个和第二个解决方案的性能会非常糟糕。