2015-03-08 56 views
2

我执行了一个小测试来确定访问指针vs值向量向量的行为。事实证明,对于小型内存块而言,两者的表现同样出色,但是,对于大型内存块而言,则会有显着差异。向量指针vs向量值对大型内存块和小型内存块的性能损失

这种行为的解释是什么?

对于下面的代码,在我的电脑上执行,D = 0的差值约为35%,而对于D = 10它是不明显的。

int D = 0; 
int K = 1 << (22 - D); 
int J = 100 * (1 << D); 

int sum = 0; 
std::vector<int> a(K); 
std::iota(a.begin(), a.end(), 0); 
long start = clock(); 
for (int j = 0; j < J; ++j) 
    for (int i = 0; i < a.size(); ++i) 
     sum += a[i]; 
std::cout << double(clock() - start)/CLOCKS_PER_SEC << " " << sum << std::endl; 

sum = 0; 
std::vector<int*> b(a.size()); 
for (int i = 0; i < a.size(); ++i) b[i] = &a[i]; 
start = clock(); 
for (int j = 0; j < J; ++j) 
    for (int i = 0; i < b.size(); ++i) 
     sum += *b[i]; 
std::cout << double(clock() - start)/CLOCKS_PER_SEC << " " << sum << std::endl; 
+0

指针会导致间接寻址,从而导致额外的内存访问。以及更多的空间正在使用当然可能会溢出缓存。 – 2015-03-08 19:04:21

+0

'sum + = a [i];'通过溢出有符号'int'的边界导致未定义的行为。 (并且另外发生相同的错误)。即使您认为这不应该成为问题,但使用不调用未定义行为的代码进行测试也是一个不错的主意。这可以通过改变为'unsigned int sum = 0;'(或其他一些无符号类型,尽管选择的类型可能会影响基准)来解决。 – 2015-03-08 19:39:45

+0

如果您不处于最佳优化级别,基准并不意味着太多。如果使用“-O3”,则可能需要采取其他措施来防止优化循环。使'sum'变成'volatile'应该这样做,但是最好仔细检查生成的程序集以确保时序代码不被重新排序。你也可以使用C++ 11的[高分辨率时钟](http://stackoverflow.com/a/5524138/1505939)而不是'clock'。 – 2015-03-08 19:45:13

回答

1

从全局内存中获取数据的速度很慢,所以CPU有一小段真正快速的内存来帮助内存访问跟上处理器。处理内存请求时,您的计算机将通过请求所请求的位置周围的所有请求并将它们存储在缓存中,尝试加速将来对内存中单个整数或指针的请求。一旦快速内存满了,每当有新内容被请求时,必须摆脱其最不喜欢的位。

您的小问题可能完全适合或基本上适用于缓存,因此内存访问速度非常快。大问题不能适应这个快速记忆,所以你有一个问题。矢量被存储为K个连续的存储位置。当你访问一个int的矢量时,它会加载int和他的一些附近值,这些值可以立即使用。但是,当您加载一个int*时,它会加载一个指向实际值的指针以及其他几个指针。这占用了一些记忆。然后,当您使用*取消引用时,它会加载实际值并可能附近有实际值。这占用了更多的内存。您不仅需要执行更多的工作,而且还要更快地填充内存。实际增加的时间会有所不同,因为它高度依赖于架构,操作(在这种情况下为+)和内存速度。另外,你的编译器会很努力地减少延迟。