有多少对象(包含 std::vectors)加载到 L1/L2/L3 缓存中
How much of an object (containing std::vectors) is loaded in to the L1/L2/L3 cache?
请参阅以下链接,第 22 页起:
http://research.scee.net/files/presentations/gcapaustralia09/Pitfalls_of_Object_Oriented_Programming_GCAP_09.pdf
上面的链接建议我是否有一个包含向量/数组的对象,如下所示:
class MyClass{
public:
double a[1000];
double b[1000];
};
下面的代码遍历 MyClass 的一个向量,并在 std::vector b 上执行一些数学运算:
std::vector<MyClass> y;
y.populateVector();
for(auto x : y){
//Iterate though x.b and do some math;
for(int i=0; i<1000; i++){
std::cout << x.b[i] << std::endl;
}
}
当我们检索每个 MyClass 对象时,来自两个数组的所有数据都将加载到缓存行中。这是真的吗?我不认为a
的数据会被加载到缓存行中,因为访问b
的地址将被计算和加载。
我试图了解与处理所需的有用数据相比,缓存中加载了多少 MyClass 对象?
我可以理解第一个b
元素是否与最后一个元素共享相同的缓存行a
但我不认为整个对象会加载到 L2/L3 缓存中只是为了处理对象的一部分?
您的声明:
for(auto x : y) ...
将x
声明为值而不是引用。 编译器可能会优化将y
的每个元素复制到局部变量x
,但我不会指望它。
如果你写:
for(auto &x : y) ...
然后循环将处理对 y
中对象的引用。 我假设这就是你的意思。
具体来说,忽略结构填充:编译器将转换
double temp = y[i].b[j];
变成等效的东西
double temp = *(
y.data() + i * sizeof(MyClass) // start of y[i]
+ 1000 * sizeof(double) // skip over y[i].a
+ j * sizeof(double)); // get to the right place in y[i].b
它会将包含该地址的缓存行大小的块加载到缓存行中。
然后,当您迭代y[i].b
的更多元素时,其中许多元素已经存在于缓存中。
由于每个数组包含 1000 个元素,因此它们比典型 CPU 上的缓存行大得多。 1000 个双精度占用 8000 字节,而 Sandy Bridge 体系结构上的缓存行(例如)为 64 字节。 遍历数组将有效地使缓存饱和。 您可能会在 x.a
的第一个和最后一个元素上浪费部分缓存行,但影响应该很小。 随着数组大小的增加,这些浪费负载的重要性接近 0。
Playstation文章讨论了大小与缓存行相当的对象。 对于像您这样的大型对象,这些优化并不重要。
取决于系统上内存的组织方式。如果碰巧a
和b
的后备阵列位于内存中非常近的位置(因为 CPU 通常会发出更大的读取来填充缓存,希望您使用它),则它们可能会被加载。如果没有,我认为没有理由阅读b
除了尝试从类实际驻留在内存中的位置读取一些指针之外,a
没有任何意义。
它确实表明,以随意的方式使用类可以并且将导致缓存未命中,因为它们驻留在内存中的方式。
加载到缓存中的一般规则是,如果 CPU 发出读取并错过缓存,它将从主内存加载缓存对齐的块(在示例中为 128 字节)。
对于您编辑的示例,是的,这些是共置的内存片段,如果仅仅因为它们在内存中的位置而发出对b
的读取,则可能会加载a
部分。
对于您的示例,每个 MyClass
对象都包含一个 2000 * sizeof(double)
字节的连续区域(很可能对齐)。这些对象被打包到由向量指向的连续内存区域中。访问每个对象的b
成员将导致缓存未命中(如果未缓存)。缓存对齐的内存片段的内容将从错过缓存的每个读取加载。根据内存对齐约束和缓存大小,a
成员中的某些条目可能会加载到内存中。甚至可以假设,由于填充和对齐,您的任何MyClass
a
成员都不会被加载到缓存中(并且没有理由将它们加载到缓存中,因为它们没有被访问)。
在您引用的链接中,两个数组 a
和 b
是 4x4 矩阵,这意味着每个数组有 16 个元素。由于这是关于视频游戏的,因此它们可能是浮点数。16 个浮点数占用 64 个字节。CPU 高速缓存行为 128 字节。因此,a
的很大一部分很有可能与b[0]
在同一缓存行中。据统计,50% 的a
将位于与b[0]
相同的缓存行中。然后,读取b[0]
会将该部分a
加载到缓存中。如果您设法在 128 字节上对齐类/结构,您甚至可以保证 a
和 b
完全适合同一缓存行。
现在在您的示例中,您不使用 16 个浮点数,而是使用 1000 个双精度。这是 8000 字节,比典型的缓存行大得多。a
的最后几个元素可能与b[0]
位于同一缓存行中,但影响很小。
- 是否可以检查变量是否位于 L1/L2/L3 缓存中
- sf::Windows上的音乐:api-ms-win-crt-locale-l1-1-0.dll:无法打开共享对象文件
- 运行时检查失败 #2 - 变量"l1"周围的堆栈已损坏
- C++ API-MS-WIN-SERVICE-PRIVATE-L1-1-1.DLL依赖项问题
- 使用C++查找 L1 和 L2 缓存的大小
- C++:快速/并行计算两个"std::vector<double>"向量之间的L1距离
- openCV的EMD-L1算法计算的距离为零
- 测量 l1/l2 缓存中加载的用于读取(包括预取)的行数
- 添加 L1 缓存大小的数组.大数组绝对比短数组快
- 如何快速计算C 中向量的归一化L1和L2标准
- 优化寄存器/L1中数据的每线程复制和0填充
- 如何通过IO定时测量来查找L1高速缓存行大小
- 在C++中清除L1、L2和L3缓存的最正确方法是什么
- 为什么 C++ 不用于 L2/L3/L4 开发项目
- Api-ms-win-core-crt-l1-1-0.dll-Microsoft Visual C++2015 Redi
- 使用 L1 范数的多项式拟合
- 如果不同的核有不同的L1缓存,为什么会出现缓存乒乓和问题
- 初级:L1解析表语法错误
- 如何清除L1、L2和L3缓存
- 有多少对象(包含 std::vectors)加载到 L1/L2/L3 缓存中