2

次のコードがあるとしましょう:

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++

私の記憶が正しければ、ローカル変数のアドレスを取得すると、通常、一部の最適化が妨げられます(たとえば、レジ​​スタに格納できません)が、アドレスに対してポインタ演算が行われず、関数からエスケープされない場合、これは適用されますか?が大きい場合、 (MSVCで)change_locals宣言された場合に違いがありますか?inline__inline

特にGCCおよびMSVCコンパイラの動作に興味があります。

4

3 に答える 3

3

inline(およびそのすべてのいとこ_inline__inline...) は gcc によって無視されます。より低い最適化レベルを除いて、利点であると判断したものは何でもインライン化する可能性があります。

x86 の gcc -O3 によるコード プロシージャは次のとおりです。

        .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
于 2011-04-05T05:59:37.813 に答える
2

私の記憶が正しければ、ローカル変数のアドレスを取得すると、通常、いくつかの最適化が妨げられます (レジスタに格納できないなど) が、これは、アドレスに対してポインター演算が行われず、関数からエスケープされない場合に適用されますか?

一般的な答えは、コンパイラが背後で他の誰も値を変更しないことを保証できる場合、レジスタに安全に配置できるということです。

これは、コンパイラが最初にインライン化を実行し、次にそれら*&foo(インライン化の結果) をfooスタック上のメモリ内のレジスタに配置する必要があるかどうかを決定する前に変換するものと考えてください。

change_locals が大きい場合、インライン (MSVC では __inline) と宣言されていれば違いはありますか?

繰り返しますが、一般的に言えば、コンパイラが何かをインライン化するかどうかは、ヒューリスティックを使用して決定されます。何かをインラインにすることを明示的に指定すると、コンパイラはおそらくこれを決定プロセスに重み付けします。

于 2011-04-05T06:32:01.187 に答える
1

これを使用して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
于 2011-04-05T05:57:55.660 に答える