【发布时间】:2022-01-17 15:18:01
【问题描述】:
我有一个看起来像这样的代码块
std::vector<uint32_t> flags(n, 0);
#pragma omp parallel for
for(int i = 0; i <v; i++) {
// If any thread finds it true, its true.
// Max value of j is n.
for(auto& j : vec[i])
flags[j] = true;
}
Work based upon the flags.
是否需要互斥锁?我了解缓存一致性将确保所有写入缓冲区都是同步的,并且不会将冲突的缓冲区写入内存。其次,可以通过简单的改变来避免缓存一致性的开销
flags[j] = true;
to
if(!flags[j]) flags[j] = true;
检查 flags[j] 是否已设置将降低写入频率,因此需要缓存一致性更新。即使有任何机会 flags[j] 被读取为 false,它也只会以对 flags[j] 的一次额外写入而告终,这是可以的。
编辑:
是的,多个线程可能并且将尝试写入 flags[j] 中的同一索引。因此问题。
uint32_t 已被有意使用,而 bool 未被使用,因为并行写入布尔值可能会发生故障,因为相邻的布尔值共享相同的字节。但是即使没有互斥体,从不同线程并行写入同一个 uint32_t 也不会像布尔值一样出现故障。
FWIW,为了符合标准,我最终保留了这个或多或少符合标准的代码,但不是 100%。但是上面显示的非标准代码在测试中没有失败。我一度认为它在多套接字机器中会失败,但结果证明 x86 也提供多套接字级别缓存一致性。
#pragma omp parallel
{
std::vector<uint32_t> flags_local(n, 0);
#pragma omp parallel for
for(int i = 0; i <v; i++) {
for(auto& j : vec[i])
flags_local[j] = true;
}
// No omp directive here, as all threads
// need to traverse their full arrays.
for(int j = 0; j <n; i++) {
if(flags_local[j] && !flags[j]) {
#pragma omp critical
{ flags[j] = true; }
}
}
}
【问题讨论】:
-
n和v是什么关系? -
是否有一个可能的
j属于vec[i1]和vec[i2](所以并发写入flags[j])? -
@YSC 假设 n 和 v 之间没有关系,但是 vec[i] 的元素的值,其中 i 从 0 到 v,最多可以等于 n。
-
@Jarod42,这就是为什么我很困惑是否应该使用互斥锁,但在这种情况下,两个线程都想写 true,如果只是一个成功了,它不像一个会取消另一个。
-
@Jarod42 请注意,您不能重新分配原子向量,因为原子类型既不可移动也不可复制。从C++20开始,这个问题可以用
std::atomic_ref解决。
标签: c++ parallel-processing openmp mutex