OpenMP导致heisenbug段错误

时间:2012-05-24 00:38:18

标签: c++ c openmp

我正在尝试在OpenMP中并行化大量for-loop。大约20%的时间它运行良好,但其余的时间它崩溃与各种段错误,如;

*** glibc detected *** ./execute: double free or corruption (!prev): <address> ***

*** glibc detected *** ./execute: free(): invalid next size (fast): <address> ***

[2] <PID> segmentation fault ./execute

我的一般代码结构如下;

<declare and initialize shared variables here>
#pragma omp parallel private(list of private variables which are initialized in for loop) shared(much shorter list of shared variables) 
{
   #pragma omp for
   for (index = 0 ; index < end ; index++) {

     // Lots of functionality (science!)
     // Calls to other deep functions which manipulate private variables
     // Finally generated some calculated_values

     shared_array1[index] = calculated_value1;
     shared_array2[index] = calculated_value2;
     shared_array3[index] = calculated_value3;

   } // end for
 }

// final tidy up

}

就发生的事情而言,每个循环迭代完全独立于彼此的循环迭代,除了它们从共享矩阵中提取数据(但每次循环迭代中的不同列)。在我调用其他函数的地方,它们只是更改私有变量(虽然偶尔会读取共享变量),所以我认为它们是线程安全的,因为它们只是在处理特定线程本地的东西?对任何共享变量的唯一写入恰好发生在最后,我们将各种计算值写入一些共享数组,其中数组元素由for循环索引索引。这段代码是用C ++编写的,尽管它调用的代码是C和C ++代码。

我一直在努力找出问题的根源,但到目前为止还没有运气。如果我设置num_theads(1)它运行正常,就像我将for-loop的内容包含在一个

中一样
#pragma omp for
for(index = 0 ; index < end ; index++) { 
  #pragma omp critical(whole_loop)
  { 
      // loop body
  }
}

可能会产生相同的效果(即任何时候只有一个线程可以通过循环)。

另一方面,如果我将for-loop's内容括在​​两个critical指令中,例如

#pragma omp for
for(index = 0 ; index < end ; index++) { 
  #pragma omp critical(whole_loop)
  { 
      // first half of loop body
  }

 #pragma omp critical(whole_loop2)
  { 
      // second half of loop body
  }

}

我得到了不可预测的segfaulting。类似地,如果我在critical指令中包含每个函数调用,它仍然不起作用。

我认为问题可能与函数调用相关联的原因是因为当我使用Valgrind(使用valgrind --tool=drd --check-stack-var=yes --read-var-info=yes ./execute)以及SIGSEGing进行概要分析时,我得到疯狂加载和存储错误的数量,例如;

Conflicting load by thread 2 at <address> size <number>
   at <address> : function which is ultimately called from within my for loop

根据the valgrind manual,这正是你对种族条件的期望。当然,这种奇怪的出现/消失的问题似乎与种族条件给出的非确定性错误的种类一致,但我不明白,如果每一次给出明显竞争条件的召唤都处于关键部分。

可能出错的事情,但我认为不包括;

  • 所有private()变量都在for-loops内初始化(因为它们是线程本地的)。

  • 我检查过共享变量具有相同的内存地址,而私有变量有不同的内存地址。

  • 我不确定同步是否有帮助,但鉴于在barrier指令的进入和退出时存在隐式critical指令,我尝试了每个函数调用的代码版本在一个(唯一命名的)关键部分中,我认为我们可以排除这一点。

关于如何最好地进行的任何想法都将受到极大的赞赏。我整天都在反对这一点。显然,我不是在寻找一个“哦 - 这就是问题”类型的答案,但更多的是如何最好地进行调试/解构。

可能是一个问题或可能有用的事情;

  • 代码中有一些std :: Vectors,它们使用vector.pushback()函数来添加元素。我记得读过调整大小的向量不是线程安全的,但是向量只是私有变量,所以不能在线程之间共享。我认为这样会好吗?

  • 如果我将整个for-loop主体包含在critical指令中并慢慢缩回代码块的末尾(那么for-loop末尾的一个不断增长的区域它在临界区之外)它运行良好,直到我暴露一个函数调用,此时segfaulting重新开始。使用Valgrind分析这个二进制文件可以显示许多其他函数调用中的竞争条件,而不仅仅是我公开的函数。

  • 其中一个函数调用是GSL函数,根据Valgrind,它不会触发任何竞争条件。

  • 我是否需要在被调用的函数中明确定义私有和共享变量?如果是这样,这对OpenMP似乎有些限制 - 这是否意味着您需要为您调用的任何遗留代码提供OpenMP兼容性?

  • 并行化大for-loop而不是有效的东西吗?

  • 如果你已经读过这篇文章了,谢谢你和Godspeed。

1 个答案:

答案 0 :(得分:1)

所以没有人能够回答这个问题,但是我已经想到了这一点,我希望这可以帮助某人,因为我的系统的行为非常奇怪。

我最终致电的(C)函数之一(my_function - &gt; intermediate_function - &gt; lower_function - &gt; BAD_FUNCTION)声明了一些它的变量为static,这意味着它们保留了相同的内存地址,因此基本上是一个共享变量。有趣的是静态覆盖了OpenMP。

我发现了这一切;

  • 使用Valgrid识别错误发生的位置,并查看所涉及的具体变量。

  • 将整个for-loop定义为关键部分,然后在顶部和底部展示更多代码。

  • 和老板说话。更多的眼睛总是有帮助,尤其是因为你被迫用语言表达问题(这最终导致我打开罪魁祸首并指出声明)