多核上的虚假共享
False sharing over multiple cores
下面的程序会不会出现虚假分享?
内存
- 1个数组分成4个相等的区域:
[A1, A2, B1, B2]
- 在实际程序中整个数组可以放入L1缓存。
- 每个区域被填充为 64 字节的倍数。
步骤
1. thread 1 write to region A1 and A2 while thread 2 write to region B1 and B2.
2. barrier
3. thread 1 read B1 and write to A1 while thread 2 read B2 and write to A2.
4. barrier
5. Go to step 1.
测试
#include <vector>
#include <iostream>
#include <stdint.h>
int main() {
int N = 64;
std::vector<std::int32_t> x(N, 0);
#pragma omp parallel
{
for (int i = 0; i < 1000; ++i) {
#pragma omp for
for (int j = 0; j < 2; ++j) {
for (int k = 0; k < (N / 2); ++k) {
x[j*N/2 + k] += 1;
}
}
#pragma omp for
for (int j = 0; j < 2; ++j) {
for (int k = 0; k < (N/4); ++k) {
x[j*N/4 + k] += x[N/2 + j*N/4 + k] - 1;
}
}
}
}
for (auto i : x ) std::cout << i << " ";
std::cout << "\n";
}
结果
32 elements of 500500 (1000 * 1001 / 2)
32 elements of 1000
您的代码中存在一些错误的共享,因为 x
不能保证与缓存行对齐。填充不一定足够。在您的示例中 N
非常小,这可能是个问题。请注意您的示例 N
,最大的开销可能是工作共享和线程管理。如果 N
足够大,即 array-size / number-of-threads >> cache-line-size
,则虚假共享不是相关问题。
从代码中的不同线程交替写入 A2
在缓存使用方面也不是最佳选择,但这不是错误的共享问题。
请注意,您不需要拆分循环。如果您在一个循环中连续访问内存中的索引,一个循环就可以了,例如
#pragma omp for
for (int j = 0; j < N; ++j)
x[j] += 1;
如果你真的很小心,你可以添加 schedule(static)
,这样你就可以保证均匀连续的单词分布。
请记住,错误共享是一个性能问题,而不是正确性问题,并且只有在经常发生时才有意义。典型的错误模式是写入 vector[my_thread_index]
.
下面的程序会不会出现虚假分享?
内存
- 1个数组分成4个相等的区域:
[A1, A2, B1, B2]
- 在实际程序中整个数组可以放入L1缓存。
- 每个区域被填充为 64 字节的倍数。
步骤
1. thread 1 write to region A1 and A2 while thread 2 write to region B1 and B2.
2. barrier
3. thread 1 read B1 and write to A1 while thread 2 read B2 and write to A2.
4. barrier
5. Go to step 1.
测试
#include <vector>
#include <iostream>
#include <stdint.h>
int main() {
int N = 64;
std::vector<std::int32_t> x(N, 0);
#pragma omp parallel
{
for (int i = 0; i < 1000; ++i) {
#pragma omp for
for (int j = 0; j < 2; ++j) {
for (int k = 0; k < (N / 2); ++k) {
x[j*N/2 + k] += 1;
}
}
#pragma omp for
for (int j = 0; j < 2; ++j) {
for (int k = 0; k < (N/4); ++k) {
x[j*N/4 + k] += x[N/2 + j*N/4 + k] - 1;
}
}
}
}
for (auto i : x ) std::cout << i << " ";
std::cout << "\n";
}
结果
32 elements of 500500 (1000 * 1001 / 2)
32 elements of 1000
您的代码中存在一些错误的共享,因为 x
不能保证与缓存行对齐。填充不一定足够。在您的示例中 N
非常小,这可能是个问题。请注意您的示例 N
,最大的开销可能是工作共享和线程管理。如果 N
足够大,即 array-size / number-of-threads >> cache-line-size
,则虚假共享不是相关问题。
从代码中的不同线程交替写入 A2
在缓存使用方面也不是最佳选择,但这不是错误的共享问题。
请注意,您不需要拆分循环。如果您在一个循环中连续访问内存中的索引,一个循环就可以了,例如
#pragma omp for
for (int j = 0; j < N; ++j)
x[j] += 1;
如果你真的很小心,你可以添加 schedule(static)
,这样你就可以保证均匀连续的单词分布。
请记住,错误共享是一个性能问题,而不是正确性问题,并且只有在经常发生时才有意义。典型的错误模式是写入 vector[my_thread_index]
.