我想实现以下函数,该函数用1标记数组的某些元素。
void mark(std::vector<signed char>& marker)
{
#pragma omp parallel for schedule(dynamic, M)
for (int i = 0; i < marker.size; i++)
marker[i] = 0;
#pragma omp parallel for schedule(dynamic, M)
for (int i = 0; i < marker.size; i++)
marker[getIndex(i)] = 1; // is it ok ?
}
如果我们尝试同时在不同的线程中将同一元素的值设置为1,会发生什么?它通常会设置为1还是这个循环可能导致意外行为?
答案 0 :(得分:4)
This answer在一个基本部分是错误的(强调我的):
如果您使用不同的线程写入同一位置,则会出现竞争条件。 这不一定是未定义的行为,但仍然需要避免。
看一下OpenMP standard,第1.4.1节说(也强调我的):
如果多个线程在没有同步的情况下写入同一个内存单元,包括由于如上所述的原子性考虑而导致的情况,则会发生数据争用。同样,如果至少有一个线程从内存单元读取并且至少一个线程在没有同步的情况下写入同一个存储器单元,包括由于如上所述的原子性考虑而导致的情况,然后发生数据竞争。 如果发生数据争用,则无法指定程序的结果。
从技术上讲,OP片段处于未定义的行为领域。这意味着在从UB中删除UB之前,不能保证程序的行为。
最简单的方法是使用原子操作保护内存访问:
#pragma omp parallel for schedule(dynamic, M)
for (int i = 0; i < marker.size; i++)
#pragma omp atomic write seq_cst
marker[getIndex(i)] = 1;
但这可能会以合理的方式阻碍性能(正如@ schorsch312正确指出的那样)。
答案 1 :(得分:3)
如果您使用不同的线程写入同一位置,则会出现竞争条件。这不一定是未定义的行为,但仍然需要避免。
因为你用所有线程写一个“1”它可能没问题,但是如果你写真实数据则可能没有。
附注:为了获得良好的数值性能,您需要处理彼此不太接近的内存。如果两个线程正在写入一个高速缓存行中的两个不同元素,则该块内存将对所有其他线程无效。这将导致高速缓存未命中并且会破坏您的性能提升(并行执行可能比单线程执行更慢)。