RHEL5上的短c openmp程序随机崩溃

Random crashes with short c openmp program on RHEL5

本文关键字:程序 随机 崩溃 openmp RHEL5      更新时间:2023-10-16

我真的很困惑你们。

下面是一个小型、独立、简单的40行程序,它计算一堆数字的部分和,并定期(但随机)崩溃我正在使用的分布式内存集群上的节点。如果我生成50个运行此代码的PBS作业,其中0到4个作业的节点将崩溃。它每次都会发生在主循环的不同重复上,每次都发生在不同的节点上,没有明显的模式。节点只是在ganglia报告中"向下",我不能对它们进行ssh("没有到主机的路由")。如果我没有提交作业,而是ssh到其中一个节点上并在那里运行我的程序,如果我运气不好,它崩溃了,那么我就不再看到文本,然后看到那个节点在ganglia上死了。

该程序使用openmp进行线程处理,只有在派生出大量线程(如12个)时才会发生崩溃。

它杀死的集群是一个RHEL 5集群,其节点具有2个6核x5650处理器:

[jamelang@hooke~]$tail/etc/redhat发布
Red Hat Enterprise Linux Server 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 %06un", 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-墙-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,类似于这个问题:为什么这段代码用openMP给出SIGABRT?。使用gcc 4.8似乎可以解决OSX上的问题。但是,在RHEL5机器上使用gcc 4.8并不能解决这个问题。RHEL5计算机的GLIBC版本是2.5,而且百胜似乎没有提供更高版本,所以管理员们坚持使用2.5。
2.如果我定义了一个SIGABRT信号处理程序,它不会在RHEL5机器上捕获问题,但它会在带有gcc47的OSX上捕获问题。
3.我认为omp子句中不应该共享任何变量,因为它们都可以有私有副本,但将它们添加为共享并不会改变行为
4.无论使用何种优化级别,节点都会被杀死
5.即使我从gdb中运行程序(即在pbs文件中放入"gdb-batch-x gdbCommand Killer"),节点也会被杀死,其中"gdbCommand"是一个只有一行的文件:"run"
6.这个例子在每次重复时都会产生线程。一种策略是制作一个包含重复循环的并行块,以防止这种情况发生。然而,这对我没有帮助——这个例子只是一个更大的研究代码的代表,我不能在其中使用这种策略。

我已经无计可施了,在我的最后一根稻草上,在我智穷的时候,我已经做好了拔头发的准备,等等。有人有什么建议或想法吗?

您正在尝试对嵌套的for循环进行并行化,在这种情况下,您需要使内部循环中的变量私有,这样每个线程都有自己的变量。可以使用private子句来完成,如下例所示。

#pragma omp parallel for private(j)
for (i = 0; i < height; i++)
for (j = 0; j < width; j++)
    c[i][j] = 2;  

在您的情况下,indexsumOfNumbers需要是私有的。