2

我在一个项目上使用 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存储线程结果时出现错误共享?

4

2 回答 2

5

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

容器不是“导致”错误共享的东西。它正在写入可能导致错误共享的对象。具体来说,在一个线程中写入与在另一个线程中访问的另一个对象位于同一“缓存行”中的对象会导致错误共享。

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

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

不要从多个线程写入数组(或向量)的相邻小元素。避免它的方法是:

  • 将数组划分为连续的段,并且只能从单独的线程访问任何单独的段。分区的大小必须至少是目标系统上高速缓存行的大小。
  • 或者,写入单独的容器,并在线程完成后合并它们。
于 2022-01-11T03:24:05.290 回答
3

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

引入了 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.

于 2022-01-11T03:47:59.913 回答