0

我正在尝试for-loop在 OpenMP 中并行化相当庞大的内容。大约 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) 它运行良好,就像我将 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
  }

}

我得到了不可预测的段错误。同样,如果我将每个函数调用都包含在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

根据valgrind 手册,这正是您对比赛条件的期望。当然,这种奇怪出现/消失的问题似乎与竞态条件会给出的非确定性错误的种类一致,但我不明白,如果每个给出明显竞态条件的调用都在关键部分中。

可能是错误但我认为不包括在内的事情;

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

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

  • 我不确定同步是否会有所帮助,但鉴于barrier指令的进入和退出有隐式指令,critical并且我已经尝试了我的代码版本,其中每个函数调用都包含在(唯一命名的)关键部分中,我认为我们可以规定出去。

任何关于如何最好地进行的想法将不胜感激。我整天都在用头撞这个。显然,我不是在寻找“哦——这就是问题所在”类型的答案,而是在寻找如何最好地进行调试/解构。

可能是个问题或可能有帮助的事情;

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

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

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

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

  • 并行化一个大的for-loop东西是行不通的吗?

  • 如果你已经读到这里,谢谢你和 Godspeed。

4

1 回答 1

1

所以没有人可以回答这个问题,但是我希望这对某人有所帮助,因为我的系统的行为是如此奇怪。

我最终调用的 ( my_function-> intermediate_function-> lower_function-> BAD_FUNCTION) 中的一个 (C) 函数将它的许多变量声明为static,这意味着它们保留了相同的内存地址,因此本质上是一个共享变量。有趣的是,静态覆盖了 OpenMP。

我发现了这一切;

  • 使用 Valgrid 确定错误发生的位置,并查看所涉及的特定变量。

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

  • 和我的老板说话。更多的眼睛总是有帮助的,尤其是因为你被迫说出问题(最终我打开了罪魁祸首函数并指向声明)

于 2012-05-24T18:05:13.837 回答