矢量会导致错误共享



我正在使用C++11进行一个项目,这里有一个函数:

void task1(int* res) {
*res = 1;
}
void task2(int* res) {
*res = 2;
}
void func() {
std::vector<int> res(2, 0); // {0, 0}
std::thread t1(task1, &res[0]);
std::thread t2(task2, &res[1]);
t1.join();
t2.join();
return res[0] + res[1];
}

函数就是这样。您可以看到有一个std::vector,它存储线程的所有结果。

我的问题是:std::vector会导致错误共享吗?如果可以的话,在使用std::vector存储线程的结果时,有什么方法可以避免错误共享吗?

std::vector会导致错误共享吗?

容器不是一种"原因;虚假分享。它对可能导致错误共享的对象进行写入。具体地说,在一个线程中写入同一"线程"中的对象;高速缓存线";因为在另一个线程中访问的另一个对象会导致错误共享。

阵列的元素在存储器中是相邻的,因此阵列的相邻小元素很可能在同一高速缓存行中。Vector是一种基于数组的数据结构。示例中访问向量元素的模式是错误共享的一个很好的例子。

在使用std::vector存储线程的结果时,有什么方法可以避免错误共享吗?

不要从多个线程写入数组(或向量(的相邻小元素。避免这种情况的方法有:

  • 将数组划分为连续的段,并仅从单独的线程访问任何单独的段。分区的大小必须至少是目标系统上缓存行的大小
  • 或者,写入单独的容器,并在线程完成后将它们合并

是的,如果在std::vector中写入两个相邻的int元素,它们很可能都在同一个缓存行上,如果两个不同的线程同时访问该缓存行,则会导致错误共享。

C++17引入了std::hardware_destructive_interference_size,这是一种可移植的方式,可以从编译器那里获得目标平台上L1缓存行大小的提示。

因此,为了防止错误共享,您应该确保两个int变量至少相隔std::hardware_destructive_interference_size字节:

void func() {
constexpr int min_offset = std::hardware_destructive_interference_size / sizeof(int);
std::vector<int> res( min_offset + 1, 0 );
std::thread t1( task1, &res[0] );
std::thread t2( task2, &res[min_offset] );
t1.join();
t2.join();
return res[0] + res[min_offset];
}

然而,在撰写本文时,有几个编译器(尚未(支持std::hardware_destructive_interference_size。有关详细信息,请参阅此问题。

如果您想合理地确定您的代码在遥远的将来不会有错误的共享,那么您可能需要假设缓存大小是std::hardware_destructive_interference_size报告的大小的两倍。

最新更新