是的,这是一个功课问题,我只需要朝着正确的方向努力
C ++的哪个代码块更快,为什么?我认为它是最顶级的,因为[i]数组正在按顺序使用,或者我错在这里?。
double A[100][100];
...
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
A[i][j] = i * j;
}
}
double A[100][100];
...
for (int j = 0; j < 100; j++) {
for (int i = 0; i < 100; i++) {
A[i][j] = i * j;
}
}
答案 0 :(得分:8)
如果不运行和分析代码,就无法知道哪条代码更快。
我们可以猜测位置和缓存行为将如何影响该时间(并且您的猜测是好的),但猜测并不能代替分析。 (见:How can I profile C++ code running in Linux?)
第一个版本可能更快的一个原因:
为什么没有区别:
我无法想到为什么第二个会更快,但我之前感到惊讶。
答案 1 :(得分:5)
最常见的答案是:您需要对两个块进行分析并根据经验查看结果。
但是,我可以为大多数具有分层缓存的现代x86,x64,PPC和ARM处理器提供答案。在这些平台上,由于更好的数据局部性,最上面的一个会更快:它按顺序访问内存地址,因此您将更频繁地访问数据缓存。智能x86和x64实现甚至会注意到您正在以这种方式顺序读取内存,并在需要之前预取下一个缓存行。底部模式在远程地址上不间断地访问内存,这意味着您可能会在每次读取时错过缓存。
Ulrich Drepper has a nice paper about this。他在该论文中的一个例子展示了这两个代码块的确切区别。
作为这里的数学示例,假设您正在编写具有64字节高速缓存行大小的Intel Corei7和32kb L1数据高速缓存。这意味着每次获取地址时,处理器也将获取该64字节对齐块中的所有其他数据。在该平台上,double是8个字节,因此每个缓存行适合8个字节。因此,顶部示例平均会错过八次迭代中的一次:在每次未命中之后,接下来的56个字节也将被提取,因此接下来的七次双*读取将在缓存中。
底部示例可能同时将100行数据(每个i
一个)放入缓存中:100 * 64 = 6400字节,完全在缓存大小内。但是你也可能超过associative cache,意味着两条线将映射到L1中的同一个SRAM,这意味着一条线将驱逐另一条线。