C中的编码:临时局部变量的效率

时间:2014-05-02 20:38:11

标签: c function variables coding-style

我想知道:用C编程,让我们说我们有两个功能:

int get_a_value();
int calculate_something(int number);

第三个版本的两个版本:

/* version 1 */
int main()
{
    int value = get_a_value();
    int result = calculate_something(value);
    return result;
}

/* version 2 */
int main()
{
    int result = calculate_something(get_a_value());
    return result;
}

从理论上讲,在正确性,内存使用和效率方面,同一事物的这两个版本之间会有什么区别?他们会产生不同的指示吗?另一方面,在什么情况下会使现实中的可能差异显着?

提前致谢。

3 个答案:

答案 0 :(得分:3)

复制这两个版本,使用gcc -S编译每个版本以获取机器语言输出,并使用sdiff进行并排比较。

使用gcc版本4.1.2 20070115(SUSE Linux)的结果:

没有优化:

main:                                         main:
.LFB2:                                        .LFB2:
        pushq   %rbp                                  pushq   %rbp
.LCFI0:                                       .LCFI0:
        movq    %rsp, %rbp                            movq    %rsp, %rbp
.LCFI1:                                       .LCFI1:
        subq    $16, %rsp                             subq    $16, %rsp
.LCFI2:                                       .LCFI2:
        movl    $0, %eax                              movl    $0, %eax
        call    get_a_value                           call    get_a_value
        movl    %eax, -8(%rbp)              |         movl    %eax, %edi
        movl    -8(%rbp), %edi              <
        movl    $0, %eax                              movl    $0, %eax
        call    calculate_something                   call    calculate_something
        movl    %eax, -4(%rbp)                        movl    %eax, -4(%rbp)
        movl    -4(%rbp), %eax                        movl    -4(%rbp), %eax
        leave                                         leave
        ret                                           ret

基本上,一个额外的移动指令。两者都分配相同数量的堆栈空间(subq $16, %rsp为堆栈保留16个字节),因此在内存方面没有区别。

1级优化(-O1):

main:                                       main:
.LFB2:                                      .LFB2:
        subq    $8, %rsp                              subq    $8, %rsp
.LCFI0:                                     .LCFI0:
        movl    $0, %eax                              movl    $0, %eax
        call    get_a_value                           call    get_a_value
        movl    %eax, %edi                            movl    %eax, %edi
        movl    $0, %eax                              movl    $0, %eax
        call    calculate_something                   call    calculate_something
        addq    $8, %rsp                              addq    $8, %rsp
        ret                                           ret

没有差异。

结果使用gcc版本2.96 20000731(Red Hat Linux 7.2 2.96-112.7.2):

没有优化:

main:                                         main:
        pushl   %ebp                                  pushl   %ebp
        movl    %esp, %ebp                            movl    %esp, %ebp
        subl    $8, %esp                              subl    $8, %esp
                                             >        subl    $12, %esp
                                             >        subl    $4, %esp
        call    get_a_value                           call    get_a_value
                                             >        addl    $4, %esp
        movl    %eax, %eax                            movl    %eax, %eax
        movl    %eax, -4(%ebp)               |        pushl   %eax
        subl    $12, %esp                    <
        pushl   -4(%ebp)                     <
        call    calculate_something                   call    calculate_something
        addl    $16, %esp                             addl    $16, %esp
        movl    %eax, %eax                            movl    %eax, %eax
        movl    %eax, -8(%ebp)               |        movl    %eax, -4(%ebp)
        movl    -8(%ebp), %eax               |        movl    -4(%ebp), %eax
        movl    %eax, %eax                            movl    %eax, %eax
        leave                                         leave
        ret                                           ret

大致相同数量的指令,订购略有不同。

1级优化(-O1):

main:                                         main:
        pushl   %ebp                                    pushl   %ebp
        movl    %esp, %ebp                              movl    %esp, %ebp
        subl    $8, %esp                      |         subl    $24, %esp
        call    get_a_value                             call    get_a_value
        subl    $12, %esp                     |         movl    %eax, (%esp)
        pushl   %eax                          <
        call    calculate_something                     call    calculate_something
        leave                                           leave
        ret                                             ret

看起来第二个版本保留了更多的堆栈空间。

因此,对于这些特定编译器的特定示例,两个版本之间没有太大差异。在这种情况下,由于以下原因,我赞成第一个版本:

  1. 在调试器中更容易跟踪;您可以检查get_a_value返回的值,然后再将其传递给calculate_something;
  2. 如果calculate_something对某些输入的表现不佳,它会为您提供一个进行健全检查的地方;
  3. 眼睛看起来更容易一些。
  4. 请记住,简洁并不一定意味着快速高效,以及一个人的快速/高效特定的编译器/硬件组合可以在不同的编译器/硬件组合下无可救药地被破坏。有些编译器实际上更容易优化以清晰方式编写的代码。

    您的代码应按顺序:

    1. 正确 - 无关紧要如果它不符合要求,它的使用速度有多快或占用的内存少;
    2. 安全 - 无关紧要如果它是恶意软件传播或将敏感数据暴露给未经授权方的风险,它的使用速度有多快或占用的内存少(是的,我&# 39;我在谈论Heart-frickin&#39; -bleed);
    3. 健壮 - 它没有消息它的速度有多快或如果因为有人在另一个房间打喷嚏而倾倒核心的内存有多少;
    4. 可维护 - 无关紧要它的速度有多快,或者如果由于需求发生变化而必须废弃和重写,它会占用多少内存(他们这样做);
    5. 高效 - 现在你可以开始担心性能和效率。

答案 1 :(得分:2)

我进行了一些测试,为2个版本生成了汇编代码。简单地从bash运行diff命令表明第一个版本比第二个版本有2个指令 如果您想自己尝试,只需使用此命令进行编译

gcc -S main.c -o asmout.s
gcc -S main2.c -o asmout2.s

然后检查与

的差异
diff asmout.s asmout2.s

我为第一个提供了更多这两条指令:

movl    %eax, -8(%rbp)
movl    -8(%rbp), %eax

编辑:
正如Keith Thompson建议使用优化选项编译时,生成的汇编代码对于两个版本都是相同的。

答案 2 :(得分:1)

这实际上取决于平台和编译器,但是通过优化它们通常应该生成相同的代码。在最坏的情况下,版本1将为额外的int分配空间。如果将get_a_value的值放在变量中会使代码更具可读性,那么我会继续这样做。我建议不这样做的唯一一次是深度递归功能。