0

我有一个程序可以读取大约 1000 张图像并创建其内容的统计摘要。每个图像都使用 OpenMP 在其自己的线程中处理,并且我将线程限制设置为与我的处理器数量相匹配。

直到大约两周前,该程序运行良好。但是,现在,如果我多次运行该程序,我的系统就会变慢并最终死机。

为了排除故障,我编写了下面列出的简单代码来模拟我的程序正在做什么。在尝试仅读取第 35 行的几个文件之后,此代码将冻结我的系统,就像我的原始程序一样。

我运行该程序,在每次失败后相继恢复到较早的内核,发现它在所有 3.6 内核到版本 3.6.8 时都失败了。

但是,当我回到内核 3.5.6 时,它可以工作。

测试.cc:

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <vector>
  4 #include <unistd.h>
  5 
  6 using namespace std;
  7 
  8 int main ()
  9 {
 10     // number of files
 11     const size_t N = 1000;
 12     // total system memory
 13     const size_t MEM = sysconf (_SC_PHYS_PAGES) * sysconf (_SC_PAGE_SIZE);
 14     // file size
 15     const size_t SZ = MEM/N;
 16 
 17     // create temp filenames
 18     vector<string> fn (N);
 19     for (size_t i = 0; i < fn.size (); ++i)
 20         fn[i] = string (tmpnam (NULL));
 21 
 22     // write a bunch of files to disk
 23     for (size_t i = 0; i < fn.size (); ++i)
 24     {
 25         vector<char> a (SZ);
 26         FILE *fp = fopen (fn[i].c_str (), "wb");
 27         fwrite (&a[0], a.size (), 1, fp);
 28         clog << fn[i] << " written" << endl;
 29     }
 30 
 31     // read a bunch of files from disk
 32 #pragma omp parallel for
 33     for (size_t i = 0; i < fn.size (); ++i)
 34     {
 35         vector<char> a (SZ);
 36         FILE *fp = fopen (fn[i].c_str (), "rb");
 37         fread (&a[0], a.size (), 1, fp);
 38         clog << fn[i] << " read" << endl;
 39     }
 40 
 41     return 0;
 42 }       

生成文件:

  1 a:$
  2     g++ -fopenmp -Wall -o test -g test.cc$
  3     ./test$

我的问题是:内核 3.6 有什么不同会导致该程序失败,但不会导致它在 3.5 版中失败?

4

2 回答 2

1

无需通过代码,如果您想对进程设置一些限制,请查看cgroups以限制资源使用。

至于冻结 - 您正在尝试一次将 GB 的数据读/写到磁盘。鉴于当今硬盘驱动器的速度约为 100MB/s,我预计内核决定将缓存刷新到磁盘时会冻结——这可能会在您尝试读取合理大小的数据块时立即发生从内存压力下的磁盘(由于您分配了大量内存,缓存空间有限)。

您可以尝试mmap()更改文件或更改内核 I/O 调度程序。

于 2012-11-28T16:34:25.107 回答
0

我没有深入研究您的代码,但我意识到一些不好的做法(至少,我认为它们是):

  • 首先,criticalopenmp 循环内的部分。这是一个同步点,把它放在每次迭代中对我来说听起来有点问题。由于每个线程必须确保没有其他线程进入那里,因此同步引入的开销可能会随着线程数的增加而增加。

  • 第二:我不太习惯 C++,但我猜每次vector<char> a (SZ)执行时都会分配内存(并在块的末尾释放)。如果我错了,请原谅。由于您事先知道 SZ 的值,因此您最好vector<vector<char> >在并行区域之前分配与线程一样多的元素。然后,在并行区域中,您可以让每个线程访问其vector<char>.

于 2012-11-29T17:09:30.403 回答