代码之家  ›  专栏  ›  技术社区  ›  GG.

哪个比较快

  •  1
  • GG.  · 技术社区  · 16 年前

    只需计算两个数组的和,代码稍作修改

    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);
    
    }
    

    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 回复  |  直到 13 年前
        1
  •  25
  •   Andrew Keeton ShuggyCoUk    16 年前

    只有一种方法可以知道,那就是测试和测量 . 您需要找出瓶颈所在(CPU、内存带宽等)。

    数组中数据的大小(示例中的int)会影响结果,因为这会影响处理器缓存的使用。通常,您会发现示例2更快,这基本上意味着您的内存带宽是限制因素(示例2将以更有效的方式访问内存)。

        2
  •  9
  •   Tim Cooper    13 年前

    下面是一些使用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和内存的暂停位置,因为这段代码非常违反直觉。

        3
  •  7
  •   Jorge Córdoba    16 年前

    理论上,由于缓存优化,第二个应该更快。

    缓存进行了优化,可以携带和保存数据块,以便在第一次访问时,您可以将第一个数组的一大块数据放入缓存。在第一个代码中,当您访问第二个数组时,可能需要取出第一个数组的一些数据,因此需要更多的访问。

    在实践中,这两种方法将或多或少地花费相同的时间,因为考虑到实际缓存的大小和完全没有数据从缓存中取出的可能性,这是第一种稍好一点的方法。

    注意:这听起来很像家庭作业。在实际生活中,对于这些大小,第一个选项将稍微快一点,但这仅适用于此具体示例,嵌套循环、更大的数组或特别是更小的缓存大小将根据顺序对性能产生重大影响。

        4
  •  5
  •   user151323    16 年前

    第一个会更快。编译器不需要重复该循环两次。虽然工作不多,但是在增加循环变量和执行检查条件时,某些循环会丢失。

        5
  •  4
  •   UncleBens    16 年前

    对于我(gcc-o3),测量显示第二个版本的速度快了25%,这可以用更有效的内存访问模式来解释(所有内存访问都是彼此接近的,而不是到处都是)。当然,在差异变得显著之前,您需要重复操作数千次。

    我还尝试了从数字头中进行std::accumulation,这是实现第二个版本的简单方法,而且比第二个版本快了一点点(可能是由于更易于编译器的循环机制?):

    sumA = std::accumulate(a, a + 10000, 0);
    sumB = std::accumulate(b, b + 10000, 0);
    
        6
  •  2
  •   Danny T.    16 年前

    第一个会更快,因为您只循环一次从1到10000。

        7
  •  2
  •   Kirill V. Lyadvinsky    16 年前

    C++标准对此一无所知,它是依赖于实现的。看起来您正在尝试提前优化。它不应该困扰你,直到它在你的程序中不是一个瓶颈。如果是这样的话,您应该使用一些分析器来找出在特定平台上哪个分析器更快。

    在那之前,我更喜欢第一个变体,因为它看起来更可读(或更好) std::accumulate )

        8
  •  2
  •   Sadat    16 年前

    如果数据类型的大小足够大,不能同时缓存两个变量(例如1),而只能缓存单个变量(示例2),那么第一个示例的代码将比第二个示例的代码慢。 否则,第一个示例的代码将比第二个示例的代码快。

        9
  •  1
  •   Eric Bainville    16 年前

    第一个可能会更快。内存访问模式将允许(现代)CPU高效地管理缓存(预取),即使在访问两个阵列时也是如此。

    如果您的CPU允许它,并且阵列是对齐的,那么速度会快得多:使用SSE3指令一次处理4int。

        10
  •  0
  •   Michael F    16 年前

    如果你是说 a[i] 而不是 a[10000] (分别针对B和B)如果编译器执行循环分布优化,则第一个与第二个完全相同。如果没有,第二个性能会稍好一些。

    如果 A〔10000〕 这样,两个循环将执行完全相同的操作(使用简单的缓存和流优化)。

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