为什么这个 C++ 代码不快?
Why this c++ code is not faster?
所有:
我有两段代码。第一个是:
#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 秒,有人可以解释一下吗?
对于第一段代码,您使用
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];
}
在您提供的第二段代码中发生的迭代次数要少得多。
您没有看到缓存友好性优势,因为访问模式仍然太简单,即使在您预测速度较慢的版本中也是如此。
两个(或更多)并发的直线输入流是现代 CPU 可以检测到并在需要之前流入 L1 的东西。
它还可以让多个 SDRAM 组同时投入有用的工作。如果您使用的是 Linux,您将无法对其进行太多控制,因为页面是随机映射的(我认为;这仍然是真的吗?),但您可以尝试使用 mmap()
和 MAP_HUGETLB
参数,然后从分配开始尝试不同的偏移量。
如果您想了解以缓存友好顺序安排计算的优势,您或许应该在二维数组中尝试不同的访问模式。
缓存在您的示例中没有发挥重要作用。对数组的线性访问比缓存大,并且访问之间几乎没有计算,将始终受到内存带宽的限制,而不是缓存。他们根本没有足够的时间通过预取来填充。
您正在测试的是编译器将您的 four/two 循环优化为一个循环的聪明程度,或者他的聪明程度以了解您在做什么并简单地打印结果。
所有: 我有两段代码。第一个是:
#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 秒,有人可以解释一下吗?
对于第一段代码,您使用 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];
}
在您提供的第二段代码中发生的迭代次数要少得多。
您没有看到缓存友好性优势,因为访问模式仍然太简单,即使在您预测速度较慢的版本中也是如此。
两个(或更多)并发的直线输入流是现代 CPU 可以检测到并在需要之前流入 L1 的东西。
它还可以让多个 SDRAM 组同时投入有用的工作。如果您使用的是 Linux,您将无法对其进行太多控制,因为页面是随机映射的(我认为;这仍然是真的吗?),但您可以尝试使用 mmap()
和 MAP_HUGETLB
参数,然后从分配开始尝试不同的偏移量。
如果您想了解以缓存友好顺序安排计算的优势,您或许应该在二维数组中尝试不同的访问模式。
缓存在您的示例中没有发挥重要作用。对数组的线性访问比缓存大,并且访问之间几乎没有计算,将始终受到内存带宽的限制,而不是缓存。他们根本没有足够的时间通过预取来填充。
您正在测试的是编译器将您的 four/two 循环优化为一个循环的聪明程度,或者他的聪明程度以了解您在做什么并简单地打印结果。