我最近开始学习 OpenMP。尽管我已经有了一些直觉,但我仍然对如何在某些对计算物理学家非常有用的情况下进行操作有一些疑问。
我的问题:在分子模拟中寻找中心力是典型的,其中对于粒子和。此外,我们可以考虑牛顿第三定律,然后对于一个有个相互作用粒子的系统,我们只需要运行一半的粒子,因为另一个只是简单地通过改变符号获得。在 C/C++中:
for(int i=0;i<N;i++){
for(int j=i+1;j<N;j++) {
F = force(r);
f[i] += F;
f[j] -= F;
}
}
这是一个简单的例子,需要对给定的向量进行求和和减法,即使不涉及中心力,类似的例子也很常见。但是,我不确定如何有效地并行化上述循环。一种选择可能是计算所有内容,而不是只计算一半:
#pragma omp for private(F), reduction(+:f[:N])
for(int i=0;i<N;i++){
for(int j=0;j<N;j++) {
if(i!=j){
F = force(x);
f[i] += F;
f[j] -= F;
}
}
}
但这远非理想,因为 F 的计算可能非常昂贵,并且会进行两次。我看到并行化第一个代码的主要问题是减少,因为它必须用于减法reduction(-:f[:N])
和加法,reduction(+:f[:N])
以及随之而来的冲突。
我的替代方法是通过使用两个中间数组,一个用于求和组件,另一个用于减法,让我们调用它们sums_f
和subs_f
:
double *sums_f, *subs_f;
// Reserve memory for sums_f and subs_f and set them to zero
#pragma omp parallel
{
#pragma omp for reduction(+: sums_f[:N], -:subs_f[:N])
for(int i=0;i<N;i++){
for(int j=i+1;j<N;j++) {
F = force(r);
sums_f[i] += F;
subs_f[j] -= F;
}
}
}
#pragma omp for
for(int i=0;i<N;i++)
f[i] += sums_f[i] - subs_f[i];
}
但我认为对于大的内存而言,这可能会非常昂贵。
总而言之,我看不出有一种替代方法可以使它们处理效率高,而不需要几个变量或额外的步骤来降低并行化的效率。您对此类案件的处理有什么建议吗?
注:[1] 更详细的解释参见:How to compute force in multi-particle MD
更新:
20210513.1:重要提示:实际上动态分配的数组不适用于reduction
. 这可能排除了它在这种情况下的使用。
20210514.1:对于 3D 对象,我使用将每个粒子的力数据存储为行乘 3 列的 3D 维数组。但是,这意味着必须执行 3x2步骤。结果是失去了并行化的时间增益。更好的解决方案是将力数据存储为一维数组。atomic