【发布时间】:2012-06-11 22:35:14
【问题描述】:
首先,我知道这种 [type of] 问题经常被问到,所以让我先说我已经阅读了尽可能多的内容,但我仍然不知道这是什么问题。
我已经并行化了一个 massive 外部 for 循环。循环迭代的次数变化,通常在 20-150 之间,但循环体做了大量的工作,调用了很多本地密集的线性代数例程(例如,代码是源代码的一部分,而不是外部依赖项) .在循环体中,有 1000 多个对这些例程的调用,但它们完全相互独立,所以我认为这将是并行性的主要候选者。循环代码是C++,但它调用了很多用C编写的子程序。
代码如下所示;
<declare and initialize shared variables here>
#ifdef _OPENMP
#pragma omp parallel for \
private(....)\
shared(....) \
firstprivate(....) schedule(runtime)
#endif
for(tst = 0; tst < ntest; tst++) {
// Lots of functionality (science!)
// Calls to other deep functions which manipulate private variables only
// Call to function which has 1000 loop iterations doing matrix manipulation
// With no exaggeration, there are probably millions
// of for-loop iterations in this body, in the various functions called.
// They also do lots of mallocing and freeing
// Finally generated some calculated_values
shared_array1[tst] = calculated_value1;
shared_array2[tst] = calculated_value2;
shared_array3[tst] = calculated_value3;
} // end of parallel and for
// final tidy up
我相信,根本不应该有任何同步-线程访问共享变量的唯一时间是shared_arrays,它们访问这些数组中的唯一点,索引为tst.
问题是,当我增加线程数时(在多核集群上!)我们看到的速度(我们调用此循环 5 次)如下;
Elapsed time System time
Serial: 188.149 1.031
2 thrds: 148.542 6.788
4 thrds: 309.586 424.037 # SAY WHAT?
8 thrds: 230.290 568.166
16 thrds: 219.133 799.780
可能值得注意的是系统时间在 2 到 4 个线程之间的巨大跳跃,并且随着我们从 2 到 4 移动,经过的时间翻倍,然后慢慢减少。
我尝试过使用大量OMP_SCHEDULE 参数,但没有运气。这是否与每个线程大量使用 malloc/new 和 free/delete 的事实有关?这一直以 8GB 内存运行 - 但我猜这不是问题。坦率地说,系统时间的大幅增加使得线程看起来可能会阻塞,但我不知道为什么会发生这种情况。
更新 1 我真的认为错误共享会成为问题,所以重新编写了代码,以便循环将它们的计算值存储在线程本地数组中,然后将这些数组复制到最后的共享数组中。遗憾的是这并没有产生任何影响,尽管我自己几乎不相信。
按照@cmeerw 的建议,我运行了 strace -f,在所有初始化之后只有数百万行
[pid 58067] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58066] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 58065] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 57684] <... futex resumed> ) = 0
[pid 58067] <... futex resumed> ) = 0
[pid 58066] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58065] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58067] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58066] <... futex resumed> ) = 0
[pid 57684] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 58065] <... futex resumed> ) = 0
[pid 58067] <... futex resumed> ) = 0
[pid 57684] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 58066] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 58065] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58066] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 57684] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58065] <... futex resumed> ) = 0
[pid 58066] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 57684] <... futex resumed> ) = 0
[pid 58067] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 58066] <... futex resumed> ) = 0
[pid 58065] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58067] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 58066] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 57684] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58065] <... futex resumed> ) = 0
[pid 58067] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58066] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 57684] <... futex resumed> ) = 0
[pid 58067] <... futex resumed> ) = 0
[pid 58066] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58065] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 58066] <... futex resumed> ) = 0
[pid 58065] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 58066] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 57684] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 58067] futex(0x35ca58bb40, FUTEX_WAIT_PRIVATE, 2, NULL <unfinished ...>
[pid 58066] <... futex resumed> ) = -1 EAGAIN (Resource temporarily unavailable)
[pid 58065] futex(0x35ca58bb40, FUTEX_WAKE_PRIVATE, 1 <unfinished ...>
[pid 57684] <... futex resumed> ) = 0
任何人有任何想法是什么意思?看起来线程过于频繁地进行上下文切换,或者只是阻塞和解除阻塞?当我 strace 将 OMP_NUM_THREADS 设置为 0 的相同实现时,我什么都没有。比较一下,使用 1 个线程时生成的日志文件为 486 KB,使用 4 个线程时生成的日志文件为 266 MB。
换句话说,并行版本调用了额外的 4170104 行日志文件...
更新 2
按照 Tom 的建议,我尝试将线程绑定到特定处理器,但无济于事。我们在 OpenMP 3.1 中,所以我使用export OMP_PROC_BIND=true 设置环境变量。相同大小的日志文件和相同的时间范围。
更新 3
情节变厚了。到目前为止只在集群上进行了分析,我通过 Macports 安装了 GNU GCC 4.7 并首次在我的 Macbook 上编译(使用 openMP)(Apple 的 GCC-4.2.1 在启用 OpenMP 时会引发编译器错误,这就是为什么我直到现在还没有在本地编译和并行运行它)。在 Macbook 上,您基本上可以看到您所期望的趋势
C-code time
Serial: ~34 seconds
2 thrds: ~21 seconds
4 thrds: ~14 seconds
8 thrds: ~12 seconds
16 thrds: ~9 seconds
我们看到接近尾声的回报递减,但这并不奇怪,因为我们在这个测试数据上迭代的几个数据集有
所以,现在问题仍然存在 - 为什么集群的性能会如此严重地下降。今晚我将尝试不同的四核 linuxbox。集群使用 GNU-GCC 4.6.3 编译,但我不敢相信它本身会产生如此大的影响?
集群上既没有安装ltrace 也没有安装GDB(由于各种原因我无法安装它们)。如果我的 linuxbox 提供类似集群的性能,我将在那里运行相应的ltrace 分析。
更新 4
哦,天哪。我决斗将我的 Macbook Pro 引导到 Ubuntu (12.04) 并重新运行代码。这一切都在运行(这有点让人放心),但我看到了我在集群上看到的相同的、奇怪的性能不佳的行为,以及数百万个 futex 调用的相同运行。鉴于我在 Ubuntu 和 OSX 中的本地机器之间的唯一区别是软件(而且我使用相同的编译器和库 - 大概 OSX 和 Ubuntu 没有不同的glibc 实现!)我现在想知道这是否与Linux如何调度/分配线程有关。无论如何,在我的本地机器上让一切变得简单一百万倍,所以我要继续ltrace -f 看看我能找到什么。我为 forks() 关闭一个单独的进程的集群编写了一个解决方法,并在运行时提供了完美的 1/2,因此绝对有可能获得并行性......
【问题讨论】:
-
您是否在这些函数中生成任何随机数?我有一个类似的问题:glibc 在每次调用 rand 时都会进行同步。 OpenMP 解决方案:stackoverflow.com/questions/8980056/…
-
你试过在“strace -f”下运行吗?这至少应该告诉你涉及哪些系统调用......
-
如果您明确设置线程关联掩码,您能看到会发生什么吗?
-
所以 strace 输出意味着存在严重的锁争用。接下来要做的是查看通过“ltrace -f”运行的输出或使用 gdb 并查看每个线程在随机点的调用堆栈(这应该提供一些线索,说明导致这种过度锁定的原因)。
-
您的集群硬件配置是什么?它有哪些 CPU - 是多插槽 AMD64 还是 Nehalem 或更新的 Intel 系统?与在具有共享缓存的内核上运行的线程相比,不同 CPU 和/或 NUMA 节点上的线程之间的同步要昂贵得多。您可能还会遇到其他瓶颈,例如内存带宽限制(“...调用大量本地密集的线性代数例程...”提示)
标签: c++ c multithreading openmp