下面的最小示例计算从1到1000的所有数字的总和,并与OpenMP并行化。
#include <iostream>
double sum;
void do_it() {
const size_t n = 1000;
#pragma omp parallel
{
#pragma omp for
for (size_t i = 1; i <= n; ++i) {
#pragma omp atomic
sum += static_cast<double>(i);
}
}
}
int main() {
sum = 0.;
do_it();
std::cout << sum << std::endl;
return 0;
}
我尝试使用clang++-6.0.0
和g++-5.4.0
以及ThreadSanitizer进行编译。两种编译器都会在libomp.so
/ libgomp.so
中产生一些关于竞争条件的警告,我认为它们是误报,以下是关于我的代码的警告:
==================
WARNING: ThreadSanitizer: data race (pid=22081)
Read of size 8 at 0x000001555f48 by main thread:
#0 main /home/arekfu/src/foo/openmp.cc:20 (openmp+0x4be0ce)
Previous atomic write of size 8 at 0x000001555f48 by thread T11:
#0 __tsan_atomic64_compare_exchange_val ??:? (openmp+0x476470)
#1 .omp_outlined._debug__ /home/arekfu/src/foo/openmp.cc:12 (openmp+0x4be011)
#2 .omp_outlined. /home/arekfu/src/foo/openmp.cc:8 (openmp+0x4be011)
#3 __kmp_invoke_microtask ??:? (libomp.so.5+0x994b2)
Location is global '<null>' at 0x000000000000 (openmp+0x000001555f48)
Thread T11 (tid=22093, running) created by main thread at:
#0 pthread_create ??:? (openmp+0x4284db)
#1 __kmpc_threadprivate_register_vec ??:? (libomp.so.5+0x5bc1f)
#2 __libc_start_main /build/glibc-LK5gWL/glibc-2.23/csu/../csu/libc-start.c:291 (libc.so.6+0x2082f)
SUMMARY: ThreadSanitizer: data race /home/arekfu/src/foo/openmp.cc:20 in main
==================
虽然我在代码中看不到任何数据争用!
我也尝试用atomic
部分替换critical
更新,如下所示:
#pragma omp critical
{
sum += static_cast<double>(i);
}
这将更改警告,但新警告没有多大意义:
==================
WARNING: ThreadSanitizer: data race (pid=27477)
Write of size 8 at 0x000001555f48 by thread T4:
#0 .omp_outlined._debug__ /home/arekfu/src/foo/openmp.cc:13 (openmp+0x4be0a2)
#1 .omp_outlined. /home/arekfu/src/foo/openmp.cc:8 (openmp+0x4be0a2)
#2 __kmp_invoke_microtask ??:? (libomp.so.5+0x994b2)
Previous write of size 8 at 0x000001555f48 by thread T3:
#0 .omp_outlined._debug__ /home/arekfu/src/foo/openmp.cc:13 (openmp+0x4be0a2)
#1 .omp_outlined. /home/arekfu/src/foo/openmp.cc:8 (openmp+0x4be0a2)
#2 __kmp_invoke_microtask ??:? (libomp.so.5+0x994b2)
Location is global '<null>' at 0x000000000000 (openmp+0x000001555f48)
Thread T4 (tid=27482, running) created by main thread at:
#0 pthread_create ??:? (openmp+0x42857b)
#1 __kmpc_threadprivate_register_vec ??:? (libomp.so.5+0x5bc1f)
#2 __libc_start_main /build/glibc-LK5gWL/glibc-2.23/csu/../csu/libc-start.c:291 (libc.so.6+0x2082f)
Thread T3 (tid=27481, running) created by main thread at:
#0 pthread_create ??:? (openmp+0x42857b)
#1 __kmpc_threadprivate_register_vec ??:? (libomp.so.5+0x5bc1f)
#2 __libc_start_main /build/glibc-LK5gWL/glibc-2.23/csu/../csu/libc-start.c:291 (libc.so.6+0x2082f)
SUMMARY: ThreadSanitizer: data race /home/arekfu/src/foo/openmp.cc:13 in .omp_outlined._debug__
==================
这些警告是否表示真实的数据竞争,或者它们是误报?
“问题”是第20行对sum的读取操作:
std::cout << sum << std::endl; // here you are reading the value of sum
TSAN无法推断此读取与循环中的(原子)更新之间的关系之前发生线程间事件。但是当然存在这种关系,因为所有线程都在omp块的末尾同步。是的,这是一个误报。
这篇文章提供了更多信息,如何使用OpenMP避免这种误报:Can I use Thread Sanitizer for OpenMP programs?