所有: 我有两段代码。第一个是:
#include <iostream>
using namespace std;
static constexpr long long n = 1000000000;
int main() {
int sum = 0;
int* a = new int[n];
int* b = new int[n];
for (long long i=0; i<n; i++) {
a[i] = static_cast<int>(i);
}
for (long long i=0; i<n; i++) {
sum *= a[i];
sum += a[i];
}
for (long long i=0; i<n; i++) {
b[i] = static_cast<int>(i);
}
for (long long i=0; i<n; i++) {
sum *= b[i];
sum += b[i];
}
cout<<sum<<endl;
}
第二个是:
#include <iostream>
using namespace std;
constexpr long long n = 1000000000;
int main() {
int* a = new int[n];
int* b = new int[n];
int sum = 0;
for (long long i=0; i<n; i++) {
a[i] = static_cast<int>(i);
b[i] = static_cast<int>(i);
}
for (long long i=0; i<n; i++) {
sum *= a[i];
sum += a[i];
sum *= b[i];
sum += b[i];
}
cout<<sum<<endl;
}
我认为第一个程序应该比第二个程序快得多,因为它的缓存更友好。然而,事实是第二个是垃圾更快。在我的服务器上,第一个需要23秒,而第二个需要20秒,有人可以解释一下吗?
答案 0 :(得分:3)
您没有看到缓存友好性优势,因为即使在您预测速度较慢的版本中,访问模式仍然过于简单。
直接输入的两个(或更多)并发流是现代CPU可以检测到并在需要之前流入L1的东西。
它还可以允许多个SDRAM库同时进行有用的工作。如果您正在使用Linux,那么您无法控制它,因为页面是随机映射的(我认为;这仍然是真的吗?),但您可以尝试使用mmap()
MAP_HUGETLB
来分配内存参数,然后从分配的开始尝试不同的偏移量。
如果您希望看到以缓存友好的顺序排列计算的优势,您应该尝试在二维数组中使用不同的访问模式。
答案 1 :(得分:2)
缓存在您的示例中不起作用。线性访问比缓存更大的数组munch并且在访问之间几乎没有计算将总是受到存储器带的限制而不受高速缓存的限制。他们根本没有足够的时间通过预取来填补。
你正在测试的是你的编译器聪明地将你的四个/两个循环优化为一个或他的聪明才能得到你正在做的事情并简单地打印结果。
答案 2 :(得分:-1)
对于第一段代码,您使用 4个循环来完成任务。
for (long long i=0; i<n; i++) {
a[i] = static_cast<int>(i);
}
for (long long i=0; i<n; i++) {
sum *= a[i];
sum += a[i];
}
for (long long i=0; i<n; i++) {
b[i] = static_cast<int>(i);
}
for (long long i=0; i<n; i++) {
sum *= b[i];
sum += b[i];
}
而在第二个中你只使用2个循环来完成任务。
for (long long i=0; i<n; i++) {
a[i] = static_cast<int>(i);
b[i] = static_cast<int>(i);
}
for (long long i=0; i<n; i++) {
sum *= a[i];
sum += a[i];
sum *= b[i];
sum += b[i];
}
在您提供的第二段代码中,发生的迭代次数要少得多。