C语言中自加与自减效率的思考

原帖地址:http://out.bitunion.org/thread-10461486-1-2.html

在正常的C语言中,使用

 cpp 代码 [复制到剪贴板]
  1.  for (val = 0val < numval++)
  2.  for (val = numval > 0val--)
从表面上看是一样的,通常我们的目的是使循环进行num次。然而在经典的C语言效率的讨论中,往往会有人说“第一种效率较第二种而言较低”。针对这一问题,昨天试图在C99手册上搜索相关说明,但是没有找到。因此我考虑这种效率的差异是由机器执行过程中产生的。至此,我决定通过反汇编的方式分析其中的差异。

下面我们进行测试。
首先编写一段测试代码,内容较为随意,只要应用到for循环就好了。我使用的是累加...

正常编译,查看结果等就不罗嗦了。下面贴出自加与自减两段代码的汇编部分:

  代码: [复制到剪贴板]  
  1.     00000000 <test_addl>:
  2.        0:    55                       push   %ebp
  3.        1:    89 e5                    mov    %esp,%ebp
  4.        3:    83 ec 10                 sub    $0x10,%esp
  5.        6:    c7 45 fc 00 00 00 00     movl   $0x0,-0x4(%ebp)
  6.        d:    c7 45 f8 00 00 00 00     movl   $0x0,-0x8(%ebp)
  7.       14:    c7 45 fc 00 00 00 00     movl   $0x0,-0x4(%ebp)
  8.       1b:    eb 0a                    jmp    27 <test_addl+0x27>
  9.       1d:    8b 45 fc                 mov    -0x4(%ebp),%eax
  10.       20:    01 45 f8                 add    %eax,-0x8(%ebp)
  11.       23:    83 45 fc 01              addl   $0x1,-0x4(%ebp)
  12.       27:    8b 45 fc                 mov    -0x4(%ebp),%eax
  13.       2a:    3b 45 08                 cmp    0x8(%ebp),%eax
  14.       2d:    7c ee                    jl     1d <test_addl+0x1d>
  15.       2f:    8b 45 f8                 mov    -0x8(%ebp),%eax
  16.       32:    c9                       leave  
  17.       33:    c3                       ret   
  18.     00000034 <test_subl>:
  19.       34:    55                       push   %ebp
  20.       35:    89 e5                    mov    %esp,%ebp
  21.       37:    83 ec 10                 sub    $0x10,%esp
  22.       3a:    c7 45 fc 00 00 00 00     movl   $0x0,-0x4(%ebp)
  23.       41:    c7 45 f8 00 00 00 00     movl   $0x0,-0x8(%ebp)
  24.       48:    8b 45 08                 mov    0x8(%ebp),%eax
  25.       4b:    89 45 fc                 mov    %eax,-0x4(%ebp)
  26.       4e:    eb 0a                    jmp    5a <test_subl+0x26>
  27.       50:    8b 45 fc                 mov    -0x4(%ebp),%eax
  28.       53:    01 45 f8                 add    %eax,-0x8(%ebp)
  29.       56:    83 6d fc 01              subl   $0x1,-0x4(%ebp)
  30.       5a:    83 7d fc 00              cmpl   $0x0,-0x4(%ebp)
  31.       5e:    7f f0                    jg     50 <test_subl+0x1c>
  32.       60:    8b 45 f8                 mov    -0x8(%ebp),%eax
  33.       63:    c9                       leave  
  34.       64:    c3                       ret   



  代码: [复制到剪贴板]  
  1.     00000000 <test_addl>:
  2.        0:    55                       push   %ebp
  3.        1:    31 c0                    xor    %eax,%eax
  4.        3:    89 e5                    mov    %esp,%ebp
  5.        5:    31 d2                    xor    %edx,%edx
  6.        7:    8b 4d 08                 mov    0x8(%ebp),%ecx
  7.        a:    85 c9                    test   %ecx,%ecx
  8.        c:    7e 0b                    jle    19 <test_addl+0x19>
  9.        e:    66 90                    xchg   %ax,%ax
  10.       10:    01 d0                    add    %edx,%eax
  11.       12:    83 c2 01                 add    $0x1,%edx
  12.       15:    39 ca                    cmp    %ecx,%edx
  13.       17:    75 f7                    jne    10 <test_addl+0x10>
  14.       19:    5d                       pop    %ebp
  15.       1a:    c3                       ret   
  16.       1b:    90                       nop
  17.       1c:    8d 74 26 00              lea    0x0(%esi,%eiz,1),%esi
  18.     00000020 <test_subl>:
  19.       20:    55                       push   %ebp
  20.       21:    31 c0                    xor    %eax,%eax
  21.       23:    89 e5                    mov    %esp,%ebp
  22.       25:    8b 55 08                 mov    0x8(%ebp),%edx
  23.       28:    85 d2                    test   %edx,%edx
  24.       2a:    7e 0b                    jle    37 <test_subl+0x17>
  25.       2c:    8d 74 26 00              lea    0x0(%esi,%eiz,1),%esi
  26.       30:    01 d0                    add    %edx,%eax
  27.       32:    83 ea 01                 sub    $0x1,%edx
  28.       35:    75 f9                    jne    30 <test_subl+0x10>
  29.       37:    5d                       pop    %ebp
  30.       38:    c3                       ret   
  31.       39:    8d b4 26 00 00 00 00     lea    0x0(%esi,%eiz,1),%esi

上述两部分编译不一样...第二段使用-O2优化了一下...

对比一下,就能看出问题了...自减是不需要判断的,并且节约代码...
产生原因很纠结...

查阅《ARM体系结构与编程》,书中间接指出了原因:
在执行sub时,程序状态寄存器可以直接通过Z标志位对结果判断是否为0(C标志位判断溢出),从而判断循环是否结束。而在执行add时,程序状态寄存器仅仅可以通过C标志位判断是否有溢出。所以导致自加过程中需要指令mov
posted on 2012-07-22 20:03  百万军中  阅读(628)  评论(0编辑  收藏  举报