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
这些是您遇到问题的可能原因:
scatter_estimate
不是线程安全的。如果是这种情况,在函数之前使用 #pragma omp critical
就可以解决它。
bin.axial_pos_num()
或 bin.tangential_pos_num()
不是线程安全的。请也检查这些。
- 结果取决于循环的执行顺序。如果不同的
i
给出相同的索引(即 bin.axial_pos_num()
和 bin.tangential_pos_num()
),那么不同的执行顺序可能会给出不同的结果,因为你以不同的顺序覆盖了 viewgram
数组的相同元素.
请注意,如果是这种情况,则可能是您的算法存在缺陷。
当 运行 有很多线程时,我的程序出现了不可预测的错误,我试图通过插入关键部分来找出问题所在。现在我有以下内容
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
这些是您遇到问题的可能原因:
scatter_estimate
不是线程安全的。如果是这种情况,在函数之前使用#pragma omp critical
就可以解决它。bin.axial_pos_num()
或bin.tangential_pos_num()
不是线程安全的。请也检查这些。- 结果取决于循环的执行顺序。如果不同的
i
给出相同的索引(即bin.axial_pos_num()
和bin.tangential_pos_num()
),那么不同的执行顺序可能会给出不同的结果,因为你以不同的顺序覆盖了viewgram
数组的相同元素. 请注意,如果是这种情况,则可能是您的算法存在缺陷。