OpenMP并行区域中的std::vector push_back会导致错误共享吗
Would this std::vector push_back in OpenMP parallel region result in false-sharing?
下面的示例代码是我的工作代码的简化版本。在这段代码中,只在调用std::vector::push_back
的最后一行写入共享变量。
std::vector<struct FortyByteStruct> results;
#pragma omp parallel for num_threads(8)
for (int i = 0; i < 250; i++)
{
struct FortyByteStruct result = some_heavy_work(i);
#pragma omp critical
{
results.push_back(result);
}
}
我想知道这个push_back
操作是否会导致错误共享,让我有机会通过消除它来进一步优化。在深入研究这个问题之前,我决定先做一些台架测试。
使用chrono
,我分别测量了some_heavy_work()
和关键部分的挂钟执行时间。后者的执行时间大约是前者的10^(-4)倍,所以我得出结论,无论是否涉及虚假共享,优化这一部分几乎没有任何好处。
无论如何,我仍然很好奇虚假分享是否是这里的一个问题。我是否必须查看std::vector
的内部实现?任何启发都将不胜感激。(我在VS2015上)
考虑到FortyByteStruct
可能小于缓存行(通常为64字节),在写入结果数据时可能存在一些错误共享。然而,它几乎不会产生影响,因为它将被关键部分的成本所掩盖,也会被修改vector
本身(而不是它的数据)的"真正"共享所掩盖。您不需要知道std::vector
实现的细节,只需要知道它的数据在内存中是连续的,并且它的状态(指向数据/大小/容量的指针)在向量变量本身的内存中。当多个线程以不受保护的方式访问同一缓存线上的单独数据时,错误共享通常是一个问题。请记住,错误共享不会影响正确性,只会影响性能。
错误共享的一个稍微不同的例子是,当您有一个std::vector<std::vector<struct FortyByteStruct>>
,并且每个线程执行一个未受保护的push_back
时。我在这里详细解释了这一点。
在您的示例中,对于已知的向量总大小,最好的方法是在循环之前调整向量的大小,然后只指定results[i] = result
。这避免了关键部分,并且OpenMP通常以几乎没有错误共享的方式来分配循环迭代。此外,还得到了results
的一个确定性阶。
也就是说,当你通过测量确认时间由some_heavy_work
支配时,你就没事了。
我不是std::vector实现方面的专家,但我确信它不会检查另一个进程是否同时在写。
尽管如此,这里有两条建议:
-
是的,关键操作的开销很小,但与并行执行"some_heavy_work"的增益相比可以忽略不计(我想…)。因此,毫无疑问,我会将其保留在中
-
您应该检查关键和原子之间的区别(openMP,原子与关键?)。
希望它能帮助
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 是否可以通过C++扩展强制多个python进程共享同一内存
- 在cuda线程之间共享大量常量数据
- 如何从具有移动语义的类对象中生成共享指针
- 在c代码之间共享数据的最佳方式
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 将静态库链接到不带-fPIC的共享库中
- 为什么std::互斥需要很长的、非常不规则的时间来共享
- 使用Boost Interprocess创建托管共享内存需要很长时间
- 无法在Ubuntu上将共享库与Eclipse链接
- 从python调用openMP共享库时,未定义opnMP函数
- 在为LINUX创建共享库时,如何避免STL的私有/弱副本
- 为什么我的共享库中存在展开符号
- 使用共享指针的函数调用,其对象应为 const
- 具有两个独占锁组的共享锁
- 共享队列的线程安全
- 共享指针和具有自定义删除程序的唯一指针之间的语法差异背后的任何原因
- 多个"常量引用"变量可以共享同一个内存吗?
- 推导 std::vector::back() 的返回类型
- 如何访问由共享指针保存的类方法?