openmp:for 循环和临界区中的竞争条件

openmp: race condition in for loop and critical section

当 运行 有很多线程时,我的程序出现了不可预测的错误,我试图通过插入关键部分来找出问题所在。现在我有以下内容

void A::func()
{
 #pragma omp parallel for
 for (int i=0; i<100; ++i)
  {
    #pragma omp critical
    results[i] = compute(i);
  }
}

这仍然是我试图找到的问题。我让它消失的唯一方法是删除 for 循环的并行化(不是很理想!)。

注意compute函数比较复杂,实际使用了一些缓存。如果该代码中存在问题,我不会感到惊讶,但由于整个代码现在处于关键部分,我无法理解发生了什么。

我尝试调试的实际 for 循环位于 https://github.com/UCL/STIR/blob/49c65340a2116834fea6a9a5cb17f73eb4a6f259/src/scatter_buildblock/ScatterSimulation.cxx#L179-L198. The test version that still has the problem (close to the example above) is here

注意:我最初是在一个更复杂的场景中发布这个的,但我删除了 that post,因为 1) 我的简单示例没有出现问题,并且 2) 这个场景更加令人费解。

添加了更多信息。我正在使用 Ryzen 9 5900 在 Windows 10 上尝试此操作。我的测试代码显示 WSL2/Ubuntu 20.04 上的 gcc-8 和 gcc-9 偶尔会出现问题。 (我发现 windows 上的 VS 2019 和 WSL2/Ubuntu 20.04 上的 clang-13 都没有问题,但我猜这并不意味着什么。

这是来自 github 的相关代码片段。

#ifdef STIR_OPENMP
#pragma omp parallel for reduction(+:total_scatter) schedule(dynamic)
#endif

    for (int i = 0; i < static_cast<int>(all_bins.size()); ++i)
    {
        const Bin bin = all_bins[i];
        const double scatter_ratio = scatter_estimate(bin);

#if defined STIR_OPENMP 
#  if _OPENMP >= 201107
#    pragma omp atomic write
#  else
#    pragma omp critical(ScatterSimulationByBin_process_data_for_view_segment_num)
#  endif
#endif
        viewgram[bin.axial_pos_num()][bin.tangential_pos_num()] =
                static_cast<float>(scatter_ratio);
        total_scatter += static_cast<double>(scatter_ratio);
    } // end loop over bins

这些是您遇到问题的可能原因:

  1. scatter_estimate 不是线程安全的。如果是这种情况,在函数之前使用 #pragma omp critical 就可以解决它。
  2. bin.axial_pos_num()bin.tangential_pos_num() 不是线程安全的。请也检查这些。
  3. 结果取决于循环的执行顺序。如果不同的 i 给出相同的索引(即 bin.axial_pos_num()bin.tangential_pos_num()),那么不同的执行顺序可能会给出不同的结果,因为你以不同的顺序覆盖了 viewgram 数组的相同元素. 请注意,如果是这种情况,则可能是您的算法存在缺陷。