【问题标题】:Can/do C compilers optimize out adress-of in inline functions?C 编译器可以/是否优化内联函数中的地址?
【发布时间】:2011-04-05 05:43:02
【问题描述】:

假设我有以下代码:

int f() {
  int foo = 0;
  int bar = 0;

  foo++;
  bar++;

  // many more repeated operations in actual code
  foo++;
  bar++;

  return foo+bar;
}

将重复的代码抽象成一个单独的函数,我们得到

static void change_locals(int *foo_p, int *bar_p) {
  *foo_p++;
  *bar_p++;
}

int f() {
  int foo = 0;
  int bar = 0;

  change_locals(&foo, &bar);
  change_locals(&foo, &bar);

  return foo+bar;
}

我希望编译器内联change_locals 函数,并将生成的代码中的*(&foo)++ 等内容优化为foo++

如果我没记错的话,获取局部变量的地址通常会阻止一些优化(例如,它不能存储在寄存器中),但是当没有对地址进行指针运算并且它不会从功能?对于更大的change_locals,如果将其声明为inline(MSVC 中的__inline)会有所不同吗?

我对 GCC 和 MSVC 编译器的行为特别感兴趣。

【问题讨论】:

  • 最好的办法是尝试检查发出的程序集。

标签: c micro-optimization


【解决方案1】:

inline(及其所有表亲_inline__inline...)被 gcc 忽略。它可能内联任何它认为是优势的东西,但在较低的优化级别除外。

gcc -O3 for x86的代码过程是:

        .text
        .p2align 4,,15
.globl f
        .type   f, @function
f:
        pushl   %ebp
        xorl    %eax, %eax
        movl    %esp, %ebp
        popl    %ebp
        ret
        .ident  "GCC: (GNU) 4.4.4 20100630 (Red Hat 4.4.4-10)"

它返回零,因为 *ptr++ 没有按照你的想法做。将增量更正为:

    (*foo_p)++;
    (*bar_p)++;

结果

        .text
        .p2align 4,,15
.globl f
        .type   f, @function
f:
        pushl   %ebp
        movl    $4, %eax
        movl    %esp, %ebp
        popl    %ebp
        ret

所以它直接返回 4。它不仅将它们内联,而且优化了计算。

vs 2005 中的 Vc++ 提供了类似的代码,但它也为 change_locals() 创建了无法访问的代码。我用的是命令行

/O2 /FD /EHsc /MD /FA /c /TP

【讨论】:

    【解决方案2】:

    如果我没记错的话, 局部变量的地址通常 防止一些优化(例如它 不能存储在寄存器中),但是 没有指针时是否适用 算术是对地址和 它没有从函数中逃脱?

    一般的答案是,如果编译器可以确保没有其他人会在背后更改一个值,那么它可以安全地放在寄存器中。

    把这想象成编译器首先执行内联,然后将所有这些*&foo(内联的结果)转换为简单的foo,然后再决定它们是否应该放在堆栈上的内存中的寄存器中。

    有了更大的change_locals,会不会 如果它被宣布,就会有所作为 内联(MSVC 中的 __inline)?

    同样,一般来说,编译器是否决定内联某些内容是使用启发式完成的。如果您明确指定要内联某些内容,编译器可能会将其纳入其决策过程。

    【讨论】:

      【解决方案3】:

      我已经使用这个测试了 gcc 4.5、MSC 和 IntelC:

      #include <stdio.h>
      
      void change_locals(int *foo_p, int *bar_p) {
        (*foo_p)++;
        (*bar_p)++;
      }
      
      int main() {
        int foo = printf("");
        int bar = printf("");
      
        change_locals(&foo, &bar);
        change_locals(&foo, &bar);
      
        printf( "%i\n", foo+bar );
      }
      

      他们都内联/优化了 foo+bar 的值,但也做了 为 change_locals() 生成代码(但没有使用它)。

      不幸的是,仍然不能保证他们会这样做 任何一种这样的“局部函数”。

      gcc:

      __Z13change_localsPiS_:
          pushl   %ebp
          movl    %esp, %ebp
          movl    8(%ebp), %edx
          movl    12(%ebp), %eax
          incl    (%edx)
          incl    (%eax)
          leave
          ret
      
      _main:
          pushl   %ebp
          movl    %esp, %ebp
          andl    $-16, %esp
          pushl   %ebx
          subl    $28, %esp
          call    ___main
          movl    $LC0, (%esp)
          call    _printf
          movl    %eax, %ebx
          movl    $LC0, (%esp)
          call    _printf
          leal    4(%ebx,%eax), %eax
          movl    %eax, 4(%esp)
          movl    $LC1, (%esp)
          call    _printf
          xorl    %eax, %eax
          addl    $28, %esp
          popl    %ebx
          leave
          ret
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-12-09
        • 2011-06-19
        • 2017-06-26
        • 1970-01-01
        • 2015-09-08
        • 2015-06-16
        • 1970-01-01
        相关资源
        最近更新 更多