#include <iostream>
#include <unistd.h>
using namespace std;
struct object
{
int i;
int j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
};
object *objectArray;
const int arraySize = 1920 * 1080;
int main()
{
objectArray = new object[arraySize];
while (1)
{
for (int i = 0; i < arraySize; i++)
{
objectArray[i].i = 1234;
}
sleep(1);
}
return 0;
}
我使用上面的代码来测试它。我编译了这个程序:
g++ -O3 -std=c++14 src/main.cpp -o bin/main
在当前状态下,for循环完成需要大约15 - 20 ms。
当struct对象看起来像这样时,
struct object
{
int i;
}
for循环需要大约0.8 - 1.2 ms才能完成。
当我甚至没有访问所有成员时,结构对象的大小如何以及为什么会影响性能?
答案 0 :(得分:6)
现代桌面CPU具有分层内存:对主内存的访问通过一系列缓存(例如L2和L1缓存),这些内容越来越小,越来越快。之前从未见过的数据首先加载到缓存中,然后从那里加载到CPU寄存器中,结果存储回缓存中。缓存只会稍后写回内存。
如果多个操作都影响缓存中的数据,那么在操作集结束时只需要一次写回内存,这比直接访问每个单独的主内存要快得多操作
此外,内存以大块的形式传输到缓存和从缓存传输,称为缓存行。典型的高速缓存行大小为64字节或128字节。
因此,当您的类为{ int i; }
时,访问数组的第一个元素已经为缓存带来了许多后续对象,并且只需从主内存中进行一次提取即可执行多个操作。当类很大时,一个缓存行只包含一个单个数组元素的i
成员,因此您需要访问每个数组元素的主内存。
现代处理器试图预测接下来可能需要哪个主存储器并开始以推测方式获取,但所有相同的访问主存储器都比访问缓存慢几个数量级,因此高步幅的阵列操作要贵得多。
正是出于这个原因,在优化代码(和数据!)以提高性能时考虑访问模式非常重要。这是你要考虑的结构阵列&#34; vs&#34;数组的结构&#34;。或者,正如一般智慧所说的那样,大多数时候,性能问题是数据结构选择不当的结果&#34;
答案 1 :(得分:2)
它与计算机的工作原理有关。通常,内存中彼此更接近的项目访问速度更快。这是因为处理器以对齐的块访问存储器并将其存储在其高速缓存中。如果所有整数不是彼此相邻,则它们将被展开并且不适合缓存。看看这个问题。
答案 2 :(得分:1)
虽然您习惯使用单个字节的内存,但这并不是硬件的工作方式。用于将数据移入和移出主存储器的通道被优化用于传输更大的数据块。例如,它可能传输64个连续字节的整个缓存行,即使只需要一个字节。
这非常接近您的性能数字:一个4字节int
是64字节高速缓存行的十六分之一,而较慢的代码比快速代码长十六倍。