只需稍微修改代码
即可计算两个数组的总和int main()
{
int a[10000]={0}; //initialize something
int b[10000]={0}; //initialize something
int sumA=0, sumB=0;
for(int i=0; i<10000; i++)
{
sumA += a[i];
sumB += b[i];
}
printf("%d %d",sumA,sumB);
}
OR
int main()
{
int a[10000]={0}; //initialize something
int b[10000]={0}; //initialize something
int sumA=0, sumB=0;
for(int i=0; i<10000; i++)
{
sumA += a[i];
}
for(int i=0; i<10000; i++)
{
sumB += b[i];
}
printf("%d %d",sumA,sumB);
}
哪个代码会更快。
答案 0 :(得分:25)
只有一种方法可以知道,那就是测试和衡量。您需要找出瓶颈所在的位置(CPU,内存带宽等)。
数组中的数据大小(示例中为int)会影响结果,因为这会对处理器缓存的使用产生影响。通常,您会发现示例2更快,这基本上意味着您的内存带宽是限制因素(示例2将以更有效的方式访问内存)。
答案 1 :(得分:9)
这是一些使用VS2005构建的时序代码:
#include <windows.h>
#include <iostream>
using namespace std;
int main ()
{
LARGE_INTEGER
start,
middle,
end;
const int
count = 1000000;
int
*a = new int [count],
*b = new int [count],
*c = new int [count],
*d = new int [count],
suma = 0,
sumb = 0,
sumc = 0,
sumd = 0;
QueryPerformanceCounter (&start);
for (int i = 0 ; i < count ; ++i)
{
suma += a [i];
sumb += b [i];
}
QueryPerformanceCounter (&middle);
for (int i = 0 ; i < count ; ++i)
{
sumc += c [i];
}
for (int i = 0 ; i < count ; ++i)
{
sumd += d [i];
}
QueryPerformanceCounter (&end);
cout << "Time taken = " << (middle.QuadPart - start.QuadPart) << endl;
cout << "Time taken = " << (end.QuadPart - middle.QuadPart) << endl;
cout << "Done." << endl << suma << sumb << sumc << sumd;
return 0;
}
运行此版本,后一版本通常更快。
我尝试编写一些汇编程序来击败第二个循环,但我的尝试通常较慢。所以我决定看看编译器生成了什么。这是在第二个版本中为主求和循环生成的优化汇编程序:
00401110 mov edx,dword ptr [eax-0Ch]
00401113 add edx,dword ptr [eax-8]
00401116 add eax,14h
00401119 add edx,dword ptr [eax-18h]
0040111C add edx,dword ptr [eax-10h]
0040111F add edx,dword ptr [eax-14h]
00401122 add ebx,edx
00401124 sub ecx,1
00401127 jne main+110h (401110h)
以下是注册用法:
这里有一些有趣的事情:
要真正理解为什么这么快,您需要使用英特尔的VTune性能分析器来查看CPU和内存停滞的位置,因为此代码非常违反直觉。
答案 2 :(得分:7)
理论上,由于缓存优化,第二个应该更快。
高速缓存经过优化,可以带来并保留数据块,这样第一次访问时,第一个数据块就可以进入高速缓存。在第一个代码中,当您访问第二个数组时可能需要取出第一个数组的某些数据,因此需要更多访问。
在实践中,这两种方法或多或少都会同时进行,考虑到实际缓存的大小,以及根本没有数据从缓存中取出的情况,第一种方法会更好。
注意:这听起来很像家庭作业。在这些尺寸的现实生活中,第一个选项会略快一些,但这只适用于这个具体的例子,嵌套循环,更大的数组或特别小的缓存大小会对性能产生重大影响,具体取决于顺序。
答案 3 :(得分:5)
第一个会更快。编译器不需要重复循环两次。虽然工作量不大,但在增加循环变量和执行检查条件时会丢失一些周期。
答案 4 :(得分:4)
对我来说(GCC -O3)测量显示第二个版本的速度提高了大约25%,这可以用更高效的内存访问模式来解释(所有内存访问都是彼此接近的,而不是所有内存访问)。当然,在差异显着之前,您需要重复操作数千次。
我也尝试过从数字头开始的std :: accumulate,这是实现第二个版本的简单方法,反过来比第二个版本快一点(可能是由于更多编译器友好的循环机制?): / p>
sumA = std::accumulate(a, a + 10000, 0);
sumB = std::accumulate(b, b + 10000, 0);
答案 5 :(得分:2)
第一个会更快,因为你只从一次循环到10000次。
答案 6 :(得分:2)
C ++标准对此一无所知,它依赖于实现。看起来你正试图做过早优化。在它不是你的程序的瓶颈之前,不应该打扰你。如果是这样,你应该使用一些分析器来找出哪一个在某个平台上会更快。
在此之前,我更喜欢第一个变体,因为它看起来更具可读性(或更好std::accumulate
)。
答案 7 :(得分:2)
如果数据类型大小足够大而不是缓存两个变量(例如1),而是单个变量(例2),那么第一个例子的代码将慢于第二个例子的代码。 否则,第一个示例的代码将比第二个示例快。
答案 8 :(得分:1)
第一个可能会更快。内存访问模式将允许(现代)CPU有效地管理缓存(预取),即使在访问两个数组时也是如此。
如果你的CPU允许它并且数组是对齐的话要快得多:使用SSE3指令一次处理4个int。
答案 9 :(得分:0)
如果您的意思是a[i]
而不是a[10000]
(并且分别代表b),并且如果您的编译器执行循环分配优化,则第一个将与第二个完全相同。如果没有,第二个会表现稍好。
如果需要a[10000]
,那么两个循环将执行完全相同的操作(使用简单的缓存和流优化)。
为投票的一些答案提供思考的食物:在每个版本的代码中执行了多少次添加?