哪一个会更快

时间:2009-09-09 10:36:56

标签: c++ c refactoring performance

只需稍微修改代码

即可计算两个数组的总和
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); 
}

哪个代码会更快。

10 个答案:

答案 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) 

以下是注册用法:

  • eax =用于索引数组
  • ebx =总计
  • ecx =循环计数器
  • edx =在循环的一次迭代中访问的五个整数的总和

这里有一些有趣的事情:

  • 编译器已经循环展开了五次。
  • 内存访问顺序不连续。
  • 它在循环中间更新数组索引。
  • 它将五个整数相加,然后将其加到总数中。

要真正理解为什么这么快,您需要使用英特尔的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],那么两个循环将执行完全相同的操作(使用简单的缓存和流优化)。

为投票的一些答案提供思考的食物:在每个版本的代码中执行了多少次添加?