这只是一个紧凑的测试用例,但我有一个双向量,我想填充所有成对差异的方阵(2D向量)。使用-O3优化编译时,我的计算机上大约需要1.96秒(仅从嵌套的双循环计算)。
#include <vector>
using namespace std;
int main(){
vector<double> a;
vector<vector<double> > b;
unsigned int i, j;
unsigned int n;
double d;
n=10000; //In practice, this value is MUCH bigger
a.resize(n);
for (i=0; i< n; i++){
a[i]=static_cast<double>(i);
}
b.resize(n);
for (i=0; i< n; i++){
b[i].resize(n);
b[i][i]=0.0; //Zero diagonal
}
for (i=0; i< n; i++){
for (j=i+1; j< n; j++){
d=a[i]-a[j];
//Commenting out the next two lines makes the code significantly faster
b[i][j]=d;
b[j][i]=d;
}
}
return 0;
}
但是,当我注释掉这两行时:
b[i][j]=d;
b[j][i]=d;
程序在大约0.000003秒内完成(仅从嵌套的双循环计算)!我真的没想到这两条线是限速步骤。我已经盯着这段代码一段时间了,我没有想法。任何人都可以提供任何关于如何优化这段简单代码的建议,以便大大缩短时间吗?
答案 0 :(得分:1)
当您注释掉这两行时,嵌套循环中剩下的所有内容都是继续计算d
然后丢弃结果。由于这不会对程序的行为产生任何影响,编译器将优化嵌套循环。这就是该计划几乎立即完成的原因。
事实上,我通过使用g++ -O3
编译代码两次来确认这一点,一次只使用嵌套循环中的d=a[i]-a[j]
语句,并且一次完全删除嵌套循环。发出的代码完全相同。
尽管如此,您的代码目前比以前慢,因为它缺少缓存。当您在这样的嵌套循环中访问二维数组时,如果可能,应始终安排迭代在内存中连续进行。这意味着第二个索引应该是变化更快的索引。对b[j][i]
的访问违反了此规则并错过了缓存。所以让我们改写。
在:
for (i=0; i< n; i++){
for (j=i+1; j< n; j++){
d=a[i]-a[j];
b[i][j]=d;
b[j][i]=d;
}
}
定时:
real 0m1.026s
user 0m0.824s
sys 0m0.196s
后:
for (i = 0; i < n; i++) {
for (j = 0; j < i; j++) {
b[i][j] = a[j] - a[i];
}
for (j = i+1; j < n; j++) {
b[i][j] = a[i] - a[j];
}
}
定时:
real 0m0.335s
user 0m0.164s
sys 0m0.164s