【问题标题】:Which loop has better performance? Increment or decrement? [duplicate]哪个循环的性能更好?增加还是减少? [复制]
【发布时间】:2012-02-16 15:13:31
【问题描述】:

可能重复:
Is it faster to count down than it is to count up?

哪个循环的性能更好?我从一些第二个更好的地方学到了东西。但想知道原因。

  for(int i=0;i<=10;i++)
      {
               /*This is better ?*/
      }


  for(int i=10;i>=0;i--)
      {
               /*This is better ?*/
      }

【问题讨论】:

  • 你测试过它们中的任何一个吗?
  • 你的循环有不同的界限:第一个从 0 到 9,第二个从 10 到 1。

标签: c performance


【解决方案1】:

第二个“可能”更好,因为比较 i 与 0 比比较 i 与 10 更容易,但我认为您可以使用其中任何一个,因为编译器会优化它们。

【讨论】:

  • 为什么i 与 0 的比较比与 10 的比较容易?
  • +1 让新手有些懈怠,因为他的答案是正确的
  • 因为大多数处理器都有与零比较的指令?所以他们不需要加载 i 和 10,减去它们然后与零进行比较,而只需立即将 i 与零进行比较。
  • +1 因为他无缘无故被-1
  • @funnyGraphicsThingy - 因为通常不需要显式比较来将零检测为算术或逻辑结果 - CPU 零标志已设置。那,许多指令集提供循环/重复操作,将寄存器自动倒数为零。 +1 为 OP
【解决方案2】:

我确实认为两个循环的性能有很大差异。

我想,当循环看起来像这样时,情况就不同了。

for(int i = 0; i < getMaximum(); i++)
{
}

for(int i = getMaximum() - 1; i >= 0; i--)
{
}

由于getMaximum()函数被调用一次或多次(假设它不是内联函数)

【讨论】:

    【解决方案3】:

    如果针对零的测试在硬件中进行了优化,则递减循环到零有时会更快。但这是一个微优化,你应该分析一下它是否真的值得做。编译器通常会为您进行优化,并且考虑到递减循环可以说是一种更糟糕的意图表达,您通常最好还是坚持“正常”的方法。

    【讨论】:

      【解决方案4】:

      递增和递减(INC 和 DEC,当翻译成汇编命令时)具有 1 个 CPU 周期的相同速度。

      然而,在某些(例如 SPARC)架构上,理论上第二个可能更快,因为不必从内存(或缓存)中获取 10:大多数架构都有处理优化的指令与特殊值 0 进行比较时的时尚(通常有一个特殊的硬连线 0 寄存器用作操作数,因此不必“浪费”寄存器来存储 10 以进行每次迭代的比较)。

      智能编译器(特别是如果目标指令集是 RISC)会自行检测到这一点,并且(如果您的计数器变量未在循环中使用)应用第二种“递减到 0”形式。

      请参阅答案https://stackoverflow.com/a/2823164/1018783https://stackoverflow.com/a/2823095/1018783 了解更多详情。

      【讨论】:

      • 在超标量架构上谈论一条指令的执行时间是没有意义的。桌面处理器在过去 15 年里一直是超标量(1993 年的 Pentium 是超标量)。
      • 并非所有现代硬件都是流水线的。嵌入式处理器将与这种微优化更相关。此外,OP 问题并没有特别针对现代桌面处理器,所以我认为答案应该是通用的,并且也适用于 20 年历史的处理器设计(通常仍用作嵌入式 CPU)
      • 问题在于答案取决于它。这取决于处理器 ISA 是否对一种或另一种有更好的支持。这取决于微架构是否能够利用 ISA 支持。如果编译器也能够利用(并且无法采取对策,例如将 10 保存在寄存器中,看到循环执行了已知的少量时间并完全展开它或使用辅助递减计数器) .变量太多了。在上下文中测量和测量是唯一的答案,它适用于所有应用程序。
      • 你是对的。但是,鉴于没有指定硬件类型、编译器类型或版本的组合,我宁愿指出一些通用的东西,它对于极少数这样的组合来说更快(而没有一个更慢),而不是专注于这样一个事实毫无疑问,一开始就做这些脱离上下文的微基准测试毫无意义。
      【解决方案5】:

      编译器应该将两个代码都优化为同一个程序集,所以没有区别。两者都需要相同的时间。

      一个更有效的讨论是是否

        for(int i=0;i<10;++i)  //preincrement
        {
        }
      

      会比

      更快
        for(int i=0;i<10;i++)  //postincrement
        {
        }
      

      因为理论上,后增量会执行额外的操作(返回对旧值的引用)。但是,即使这样也应该针对相同的程序集进行优化。

      如果没有优化,代码将如下所示:

         for ( int i = 0; i < 10 ; i++ )
      0041165E  mov         dword ptr [i],0 
      00411665  jmp         wmain+30h (411670h) 
      00411667  mov         eax,dword ptr [i] 
      0041166A  add         eax,1 
      0041166D  mov         dword ptr [i],eax 
      00411670  cmp         dword ptr [i],0Ah 
      00411674  jge         wmain+68h (4116A8h) 
      
         for ( int i = 0; i < 10 ; ++i )
      004116A8  mov         dword ptr [i],0 
      004116AF  jmp         wmain+7Ah (4116BAh) 
      004116B1  mov         eax,dword ptr [i] 
      004116B4  add         eax,1 
      004116B7  mov         dword ptr [i],eax 
      004116BA  cmp         dword ptr [i],0Ah 
      004116BE  jge         wmain+0B2h (4116F2h) 
      
         for ( int i = 9; i >= 0 ; i-- )
      004116F2  mov         dword ptr [i],9 
      004116F9  jmp         wmain+0C4h (411704h) 
      004116FB  mov         eax,dword ptr [i] 
      004116FE  sub         eax,1 
      00411701  mov         dword ptr [i],eax 
      00411704  cmp         dword ptr [i],0 
      00411708  jl          wmain+0FCh (41173Ch) 
      

      所以即使在这种情况下,速度也是一样的。

      【讨论】:

      • 应该优化到同一个程序集...如果在循环内使用i 会怎样?
      • @Benoit 我的意思是相同数量的指令。
      【解决方案6】:

      同样,所有微观性能问题的答案都是衡量在使用环境中衡量,而不是外推到其他环境。

      很长一段时间以来,如果没有非凡的复杂性,就不可能计算指令执行时间。

      处理器和内存速度之间的不匹配以及引入缓存以隐藏部分延迟(但不是带宽)使得一组指令的执行对内存访问模式非常敏感。这是你仍然可以通过相当高层次的思考来优化的东西。但这也意味着,如果不考虑内存访问模式,显然更糟糕的事情在完成后会变得更好。

      然后超标量(处理器可以同时做几件事的事实)和乱序执行(处理器可以在流程中的前一个指令之前执行指令的事实)使基本计数毫无意义,即使您忽略内存访问.您必须知道需要执行哪些指令(因此忽略结构的一部分是不明智的)以及如果您想获得良好的先验估计,处理器如何对指令进行分组。

      【讨论】:

        猜你喜欢
        • 2015-10-18
        • 2010-09-11
        • 2018-10-02
        • 1970-01-01
        • 2021-08-25
        • 2016-08-17
        • 2015-11-03
        • 1970-01-01
        • 2010-10-20
        相关资源
        最近更新 更多