【发布时间】:2015-11-04 00:16:49
【问题描述】:
我正在尝试并行化 (OpenMP) 一些科学 C++ 代码,其中大部分 (>95%) CPU 时间用于计算 N~ 阶的讨厌(且不可避免)O(N^2) 交互200 种不同的粒子。此计算重复 1e10 个时间步长。我用 OpenMP 尝试了各种不同的配置,每一个都比串行代码慢一些(至少一个数量级),随着添加额外的内核,扩展性很差。
下面是相关代码的草图,具有代表性的虚拟数据层次结构Tree->Branch->Leaf。每个Leaf 对象都存储自己的位置和速度,用于当前和前三个时间步长等。然后每个Branch 存储Leaf 对象的集合,每个Tree 存储Branch 对象的集合。这种数据结构非常适用于复杂但 CPU 密集度较低的计算,这些计算也必须在每个时间步执行(需要数月才能完善)。
#include <omp.h>
#pragma omp parallel num_threads(16) // also tried 2, 4 etc - little difference - hoping that placing this line here spawns the thread pool at the onset rather than at every step
{
while(i < t){
#pragma omp master
{
/* do other calculations on single core, output etc. */
Tree.PreProcessing()
/* PreProcessing can drastically change data for certain conditions, but only at 3 or 4 of the 1e10 time steps */
Tree.Output()
}
#pragma omp barrier
#pragma omp for schedule(static) nowait
for(int k=0; k < size; k++){
/* do O(N^2) calc that requires position of all other leaves */
Tree.CalculateInteraction(Branch[k])
}
/* return to single core to finish time step */
#pragma omp master
{
/* iterate forwards */
Tree.PropagatePositions()
i++
}
#pragma omp barrier
}
简单地说,CPU-hog 函数是这样做的:
void Tree::CalculateInteraction(Leaf* A){
// for all branches B in tree{
// for all leaves Q in B{
if(condition between A and Q){skip}
else{
// find displacement D of A and Q
// find displacement L of A and "A-1"
// take cross product of the two displacements
// add the cross-product to the velocity of leaf A
for(int j(0); j!=3; j++){
A->Vel[j] += constant * (D_cross_L)[j];
}
我的问题是,这种性能下降是由于 openMP 线程管理开销占主导地位,还是因为数据层次结构设计时没有考虑并行性?
我应该注意,每个步骤的并行时间都比串行时间长得多,这不是一些初始化开销问题;这两个版本已针对需要 1 小时和 10 小时的计算进行了测试,最终希望应用于可能需要 30 小时的串行计算(即使是 2 倍的加速也是非常有益的)。此外,可能值得知道我正在使用带有-fopenmp -march=native -m64 -mfpmath=sse -Ofast -funroll-loops 的g++ 5.2.0。
我是 OpenMP 的新手,因此非常感谢任何提示,如果有任何需要澄清的地方,请告诉我。
【问题讨论】:
-
树形结构的组织精确度如何?子节点是连续数组还是链表(即父节点只有指向第一个子节点的指针,每个子节点都指向她的妹妹)?我经常使用并行八叉树(用于 N 体模拟和 SPH),但总能找到出色的缩放比例。我也很好奇你需要 O(N^2) 来做什么——这通常分别通过 O(NlnN) 或 O(N) 中的树或 FMM 来近似。
-
@Walter 叶子存储在一个双向链表中,指向分支的前一个和下一个叶子(可以在内存中的“任何地方”找到)。树是这些特定类型计算的标准选择(超流氦的涡流灯丝模型,参见例如this review),但由于这个项目的时间限制,我正在寻找一个更快的解决方案来运行一组长计算.
-
@Walter 我猜你使用的是连续数组?但是您确切地知道您有多少个子节点。如果您不知道并且必须使用列表,那么内存访问可能真的会扰乱并行扩展。我想知道每个线程分配器是否会有所帮助。
-
@emsr,这仅适用于天真的程序员。看我的回答。
标签: c++ multithreading performance parallel-processing openmp