【问题标题】:How can I enhance this for loop我怎样才能增强这个for循环
【发布时间】:2014-06-18 07:40:05
【问题描述】:

我使用 sublime text 作为我的文本编辑器,而且我总是这样编写我的 for 循环:

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

最近我在查看编辑器的代码提示,我注意到它说“Enhaced For Loop”,它向我展示了这个:

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

我很好奇这是如何“增强”的,以便我可以知道以及其他任何可能好奇的人?

【问题讨论】:

  • 这可能会让你感兴趣stackoverflow.com/questions/1656506/…
  • @JosephHelfert 很有趣,谢谢分享
  • 您可能对我刚刚在 javascript 中使用两种循环格式运行了一些测试感兴趣。两种循环格式的性能相同。这是一个小样本和少量迭代,但我怀疑即使有大量迭代,它仍然会有相同的性能。
  • 这很有趣,我相信这与使用的语言有很大关系。 @barakmanos 回答中似乎确实少了一项操作
  • @armanali 我还发现,就速度而言,如果您只是想将一个值与数组中的所有值进行比较(如果这是您正在使用的),那么 for 循环就很多了更清洁地查看和阅读。我最近不得不根据数组中的所有值检查一个值,编写一个带有 for 循环的函数比编写一个 if 或 switch 语句来检查所有值要短得多。

标签: java jquery c++ performance for-loop


【解决方案1】:

我不确定“增强的 for 循环”是什么。每当我听到增强的 for 循环时,我都会想到类似 https://blogs.oracle.com/CoreJavaTechTips/entry/using_enhanced_for_loops_with

【讨论】:

    【解决方案2】:

    这是旧编译器和解释器(在旧芯片架构上)的保留,它们的加法比减法稍慢。

    现代编译器和解释器(加上现代芯片架构)确实没有这个问题。

    有关此问题的更多详细信息,请参阅JavaScript loop performance - Why is to decrement the iterator toward 0 faster than incrementing

    【讨论】:

      【解决方案3】:

      如图所示:-http://www.cis.upenn.edu/~matuszek/General/JavaSyntax/enhanced-for-loops.html

      遍历数组所有元素的常用方法 为了使用“标准”for 循环 ,例如,

      for (int i = 0; i < myArray.length; i++) {
          System.out.println(myArray[i]);
      }
      

      所谓的增强的 for 循环是一种更简单的方法来做同样的事情。 (语法中的冒号可以读作“in”)

      for (int myValue : myArray) {
          System.out.println(myValue);
      }
      

      在 Java 5 中引入了增强的 for 循环,作为一种更简单的方式来遍历集合的所有元素(这些页面中不包含集合)。它也可以用于数组,如上例所示,但这不是最初的目的。

      增强的 for 循环简单但不灵活。当您希望按从头到尾的顺序遍历数组元素时,可以使用它们,并且您不需要知道当前元素的索引。在所有其他情况下,“标准”for 循环 应该是首选。 两种额外的语句类型,breakcontinue ,还可以控制增强型 forloop 的行为。

      高级

      break 和 continue 语句可以与语句标签一起使用

      。欲了解更多信息:-

      https://blogs.oracle.com/CoreJavaTechTips/entry/using_enhanced_for_loops_with

      【讨论】:

      • 示例代码没有将 for-in 循环显示为增强循环,但是关于 for-in 增强循环的优点。
      • 可能是因为格式化,因为我无法像在移动设备上那样格式化,请格式化这个答案
      • @artlessnoise 并不是要成为一种特定的语言,而只是对哪种语言更好或更“增强”的一般知识
      • @artlessnoise 我确实添加了一些在这个问题的答案中使用次数较多的语言。
      【解决方案4】:

      第一个 for 循环与第二个 for 循环相比,包含一个额外的 Load 操作,该操作在每次迭代时执行(当 CPU 加载变量 lengthOfSomething 的值时) )。

      话虽如此,如果启用编译器优化,这个 Load 操作可能会被消除,因为变量 lengthOfSomething 的值在整个循环执行过程中保持不变。


      你可以通过比较每个循环的反汇编代码来更好地理解这一点。

      在第一个循环中,每次迭代都会执行以下操作:

      mov eax,dword ptr [i]
      add eax,1
      mov dword ptr [i],eax
      mov eax,dword ptr [i]
      cmp eax,dword ptr [lengthOfSomething]
      

      在第二个循环中,每次迭代都会执行以下操作:

      mov eax,dword ptr [i]
      sub eax,1
      mov dword ptr [i],eax
      cmp dword ptr [i],0
      

      如您所见,第一个循环包含额外的mov eax,dword ptr [i] 操作。这是因为 CPU 架构支持比较一个内存内容和一个常量,但不支持比较两个内存内容。请注意,上面示例中的反汇编代码是由 Microsoft Visual C++ 2010 的编译器生成的,并且禁用了编译器优化。但可以合理地假设其他编译器会生成类似的反汇编代码。


      好的,所以在上面的例子中,实际的改进是由于第一个循环比较两个变量(因此其中一个必须加载到寄存器中),而第二个循环比较一个变量和一个常量( CPU 架构支持的操作)。但与第一个循环相比,第二个循环中的变量访问操作更少。

      【讨论】:

      • 只有在您实际检查每次迭代的长度时才适用。从问题中的代码很难看出这一点。最佳做法是在循环之前将长度分配给变量。如果这样做了,则不会发生额外的执行。
      • @Nick Zimmerman:说什么???在第一个循环中,每次迭代都会检查条件i &lt; lengthOfSomething。此条件包含 CPU 需要加载的 2 个变量。在第二个循环中,每次迭代都会检查条件i &gt;= 0。此条件包含 CPU 需要加载的 1 变量。
      • @barak manos 如果是变量,它的值会被解释器缓存,不会每次都执行load。如果你正在做某事.length 那么你会得到额外的执行。
      • @Nick Zimmerman:翻译??????这不是 Python。如果有的话,该值可以在运行时由 CPU 缓存。但这里不是这样。案例是 - 编译器如何“选择”将此源代码转换为机器操作列表。对于i &lt; lengthOfSomething,它必须为每个变量添加一个 Load 操作(除非启用了编译器优化,正如我在答案中明确指出的那样)。在运行时,CPU 可能能够利用缓存的一些优势。它可能会,也可能不会 - 取决于缓存启发式。
      • 但整个事情只会发生在第一个循环中。在第二个循环中,这不会成为问题,因此提高了性能......明白了吗?
      【解决方案5】:

      我怀疑这个问题的答案与性能有很大关系。我用 c++ 编写了每个 for 循环的版本,并使用 GCC 来获得生成的 ASM。 “增强型 For 循环”(第 2 版)实际上比“非增强型 For 循环”多一条指令 subl $1, -8(%rbp)

      无论哪种方式,性能差异都可以忽略不计。这种代码选择应基于代码的可读性以及您是否希望将 for 循环内的操作应用于正向或反向的元素。 IE 从列表的开头或列表的结尾搜索。

      for 循环的第一个版本:

      int main(int argc, char* argv[])
      {
          int lengthOfSomething = 10;
          int valueToInc = 0;
      
          for(int i = 0; i < lengthOfSomething; i++)
          {
              valueToInc += i;
          }
      }
      

      生成的 ASM

          .file   "main.cpp"
          .text
          .globl  main
          .type   main, @function
      main:
      .LFB0:
          .cfi_startproc
          pushq   %rbp
          .cfi_def_cfa_offset 16
          .cfi_offset 6, -16
          movq    %rsp, %rbp
          .cfi_def_cfa_register 6
          movl    %edi, -20(%rbp)
          movq    %rsi, -32(%rbp)
          movl    $10, -12(%rbp)
          movl    $0, -4(%rbp)
          movl    $0, -8(%rbp)
          jmp .L2
      .L3:
          movl    -8(%rbp), %eax
          addl    %eax, -4(%rbp)
          addl    $1, -8(%rbp)
      .L2:
          movl    -8(%rbp), %eax
          cmpl    -12(%rbp), %eax
          jl  .L3
          movl    $0, %eax
          popq    %rbp
          .cfi_def_cfa 7, 8
          ret
          .cfi_endproc
      .LFE0:
          .size   main, .-main
          .ident  "GCC: (GNU) 4.8.2 20140206 (prerelease)"
          .section    .note.GNU-stack,"",@progbits
      

      for 循环的第二个版本:

      int main(int argc, char* argv[])
      {
          int lengthOfSomething = 10;
          int valueToInc = 0;
      
          for(int i = lengthOfSomething - 1; i >= 0; i--)
          {
              valueToInc += i;
          }
      }
      

      生成的 ASM

          .file   "main2.cpp"
          .text
          .globl  main
          .type   main, @function
      main:
      .LFB0:
          .cfi_startproc
          pushq   %rbp
          .cfi_def_cfa_offset 16
          .cfi_offset 6, -16
          movq    %rsp, %rbp
          .cfi_def_cfa_register 6
          movl    %edi, -20(%rbp)
          movq    %rsi, -32(%rbp)
          movl    $10, -12(%rbp)
          movl    $0, -4(%rbp)
          movl    -12(%rbp), %eax
          subl    $1, %eax
          movl    %eax, -8(%rbp)
          jmp .L2
      .L3:
          movl    -8(%rbp), %eax
          addl    %eax, -4(%rbp)
          subl    $1, -8(%rbp)
      .L2:
          cmpl    $0, -8(%rbp)
          jns .L3
          movl    $0, %eax
          popq    %rbp
          .cfi_def_cfa 7, 8
          ret
          .cfi_endproc
      .LFE0:
          .size   main, .-main
          .ident  "GCC: (GNU) 4.8.2 20140206 (prerelease)"
          .section    .note.GNU-stack,"",@progbits
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-07-14
        • 2020-01-05
        • 1970-01-01
        • 1970-01-01
        • 2020-08-14
        • 1970-01-01
        • 2017-06-25
        相关资源
        最近更新 更多