我有一个真正的益智游戏给你们。
下面是一个小的,独立的,简单的40行程序,它计算了一堆数字的部分和,并且常规地(但随机地)崩溃我正在使用的分布式内存集群上的节点。如果我生成50个运行此代码的PBS作业,则0到4之间的任务将使其节点崩溃。它将在每次主循环的不同重复上发生,并且每次在不同的节点上发生,没有可辨别的模式。节点只是在神经节报告上“向下”,我不能ssh到他们(“没有路由到主机”)。如果我没有提交作业,而是将ssh移到其中一个节点上并在那里运行我的程序,如果我运气不好而崩溃那么我就停止看文本,然后看到那个节点在神经节上死了。
该程序使用openmp进行线程化,只有在生成大量线程(如12)时才会发生崩溃。
它要杀死的集群是一个RHEL 5集群,其节点有2个6核x5650处理器:
[jamelang @ hooke~] $ tail / etc / redhat-release
红帽企业Linux服务器版本5.7(Tikanga)
我已尝试启用核心转储ulimit -c unlimited
但未显示任何文件。这是代码,带有注释:
#include <cstdlib>
#include <cstdio>
#include <omp.h>
int main() {
const unsigned int numberOfThreads = 12;
const unsigned int numberOfPartialSums = 30000;
const unsigned int numbersPerPartialSum = 40;
// make some numbers
srand(0); // every instance of program should get same results
const unsigned int totalNumbersToSum = numbersPerPartialSum * numberOfPartialSums;
double * inputData = new double[totalNumbersToSum];
for (unsigned int index = 0; index < totalNumbersToSum; ++index) {
inputData[index] = rand()/double(RAND_MAX);
}
omp_set_num_threads(numberOfThreads);
// prepare a place to dump output
double * partialSums = new double[numberOfPartialSums];
// do the following algorithm many times to induce a problem
for (unsigned int repeatIndex = 0; repeatIndex < 100000; ++repeatIndex) {
if (repeatIndex % 1000 == 0) {
printf("Absurd testing is on repeat %06u\n", repeatIndex);
}
#pragma omp parallel for
for (unsigned int partialSumIndex = 0; partialSumIndex < numberOfPartialSums;
++partialSumIndex) {
// get this partial sum's limits
const unsigned int beginIndex = numbersPerPartialSum * partialSumIndex;
const unsigned int endIndex = numbersPerPartialSum * (partialSumIndex + 1);
// we just sum the 40 numbers, can't get much simpler
double sumOfNumbers = 0;
for (unsigned int index = beginIndex; index < endIndex; ++index) {
// only reading, thread-safe
sumOfNumbers += inputData[index];
}
// writing to non-overlapping indices (guaranteed by omp),
// should be thread-safe.
// at worst we would have false sharing, but that would just affect
// performance, not throw sigabrts.
partialSums[partialSumIndex] = sumOfNumbers;
}
}
delete[] inputData;
delete[] partialSums;
return 0;
}
我用以下内容编译它:
/home/jamelang/gcc-4.8.1/bin/g++ -O3 -Wall -fopenmp Killer.cc -o Killer
它似乎与正确的共享对象相关联:
[jamelang@hooke Killer]$ ldd Killer
linux-vdso.so.1 => (0x00007fffc0599000)
libstdc++.so.6 => /home/jamelang/gcc-4.8.1/lib64/libstdc++.so.6 (0x00002b155b636000)
libm.so.6 => /lib64/libm.so.6 (0x0000003293600000)
libgomp.so.1 => /home/jamelang/gcc-4.8.1/lib64/libgomp.so.1 (0x00002b155b983000)
libgcc_s.so.1 => /home/jamelang/gcc-4.8.1/lib64/libgcc_s.so.1 (0x00002b155bb92000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003293a00000)
libc.so.6 => /lib64/libc.so.6 (0x0000003292e00000)
/lib64/ld-linux-x86-64.so.2 (0x0000003292a00000)
librt.so.1 => /lib64/librt.so.1 (0x0000003298600000)
一些注意事项:
1.在使用gcc 4.7的osx lion上,此代码将抛出一个SIGABRT,类似于这个问题:Why is this code giving SIGABRT with openMP?。使用gcc 4.8似乎解决了OSX上的问题。但是,在RHEL5机器上使用gcc 4.8无法修复它。 RHEL5机器有GLIBC版本2.5,似乎yum不提供后者,所以管理员坚持使用2.5。
2.如果我定义一个SIGABRT信号处理程序,它不能解决RHEL5机器上的问题,但它确实在OSX上用gcc47捕获它。
3.我认为不需要在omp子句中共享任何变量,因为它们都可以拥有私有副本,但将它们添加为共享不会改变行为。
4.无论使用的优化级别如何,都会发生节点的查杀
5.即使我在gdb中运行程序(即在pbs文件中放入“gdb -batch -x gdbCommands Killer”),也会发生节点被杀,其中“gdbCommands”是一行文件:“run”
6.此示例在每次重复时生成线程。一种策略是制作包含重复循环的并行块以防止这种情况。然而,这对我没有帮助 - 这个例子只代表了一个更大的研究代码,我无法使用该策略。
我完全没有想法,在我的最后一根稻草上,在我的智慧结束时,准备好把头发拉出来等等。有没有人有任何建议或想法?
答案 0 :(得分:0)
您正在尝试并行化嵌套for循环,在这种情况下,您需要将内部循环中的变量设为私有,以便每个线程都有自己的变量。可以使用private
子句来完成,如下例所示。
#pragma omp parallel for private(j)
for (i = 0; i < height; i++)
for (j = 0; j < width; j++)
c[i][j] = 2;
在您的情况下,index
和sumOfNumbers
必须是私密的。