2

次のようなループがある場合:

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

VB6 ではSlowVariable、ループの反復ごとにアクセスされるため、次の処理がはるかに効率的になります。

int cnt = SlowVariable;

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

GCC で同じ最適化を行う必要がありますか? それともSlowVariable一度だけ評価しますか?

4

6 に答える 6

2

一般に、変な種類のメモリにマップされていない限り、変数は遅く (または速く) なりませんvolatile(この場合、変数を宣言する必要があります)。

しかし、実際には、ローカル変数を使用すると最適化の機会が増え、その効果は非常に目に見える可能性があります。コンパイラは、ループ内で呼び出された関数がそのグローバル変数を読み書きできないことを証明できる場合にのみ、それ自体でグローバル変数を「キャッシュ」できます。ループ内で外部関数を呼び出す場合、コンパイラはおそらくそのようなことを証明できません。

于 2013-01-31T12:38:09.997 に答える
1

これは、コンパイラがどのように最適化するかによって異なります。たとえば、次のようになります。

#include <stdio.h>

int main(int argc, char **argv)
{
    unsigned int i;
    unsigned int z = 10;

    for( i = 0 ; i < z ; i++ )
        printf("%d\n", i);

    return 0;
}

を使用してコンパイルした場合gcc example.c -o example、結果コードは次のようになります。

 0x0040138c <+0>:     push   ebp
 0x0040138d <+1>:     mov    ebp,esp
 0x0040138f <+3>:     and    esp,0xfffffff0
 0x00401392 <+6>:     sub    esp,0x20
 0x00401395 <+9>:     call   0x4018f4 <__main>
 0x0040139a <+14>:    mov    DWORD PTR [esp+0x18],0xa
 0x004013a2 <+22>:    mov    DWORD PTR [esp+0x1c],0x0
 0x004013aa <+30>:    jmp    0x4013c4 <main+56>
 0x004013ac <+32>:    mov    eax,DWORD PTR [esp+0x1c]
 0x004013b0 <+36>:    mov    DWORD PTR [esp+0x4],eax
 0x004013b4 <+40>:    mov    DWORD PTR [esp],0x403064
 0x004013bb <+47>:    call   0x401b2c <printf>
 0x004013c0 <+52>:    inc    DWORD PTR [esp+0x1c]
 0x004013c4 <+56>:    mov    eax,DWORD PTR [esp+0x1c]  ; (1) 
 0x004013c8 <+60>:    cmp    eax,DWORD PTR [esp+0x18]  ; (2)
 0x004013cc <+64>:    jb     0x4013ac <main+32>
 0x004013ce <+66>:    mov    eax,0x0
 0x004013d3 <+71>:    leave
 0x004013d4 <+72>:    ret
 0x004013d5 <+73>:    nop
 0x004013d6 <+74>:    nop
 0x004013d7 <+75>:    nop
  1. の値がiスタックから にムービー化されeaxます。
  2. 次に、CPU はeaxorを、スタックにあるiの値と比較します。z

これはすべて、すべてのラウンドで発生します。

を使用してコードを最適化した場合gcc -O2 example.c -o example、結果は次のようになります。

 0x00401b70 <+0>:     push   ebp
 0x00401b71 <+1>:     mov    ebp,esp
 0x00401b73 <+3>:     push   ebx
 0x00401b74 <+4>:     and    esp,0xfffffff0
 0x00401b77 <+7>:     sub    esp,0x10
 0x00401b7a <+10>:    call   0x4018a8 <__main>
 0x00401b7f <+15>:    xor    ebx,ebx
 0x00401b81 <+17>:    lea    esi,[esi+0x0]
 0x00401b84 <+20>:    mov    DWORD PTR [esp+0x4],ebx
 0x00401b88 <+24>:    mov    DWORD PTR [esp],0x403064
 0x00401b8f <+31>:    call   0x401ae0 <printf>
 0x00401b94 <+36>:    inc    ebx
 0x00401b95 <+37>:    cmp    ebx,0xa                     ; (1)
 0x00401b98 <+40>:    jne    0x401b84 <main+20>
 0x00401b9a <+42>:    xor    eax,eax
 0x00401b9c <+44>:    mov    ebx,DWORD PTR [ebp-0x4]
 0x00401b9f <+47>:    leave
 0x00401ba0 <+48>:    ret
 0x00401ba1 <+49>:    nop
 0x00401ba2 <+50>:    nop
 0x00401ba3 <+51>:    nop
  1. コンパイラは、 の値をチェックする意味がないことを認識しているzため、コードを のようなものに変更しますfor( i = 0 ; i < 10 ; i++ )

zコンパイラがこのコードの like の値を認識しない場合:

#include <stdio.h>

void loop(unsigned int z) {
    unsigned int i;
    for( i = 0 ; i < z ; i++ )
        printf("%d\n", i);
}

int main(int argc, char **argv)
{
    unsigned int z = 10;

    loop(z);

    return 0;
}

結果は次のようになります。

0x0040138c <+0>:     push   esi
0x0040138d <+1>:     push   ebx
0x0040138e <+2>:     sub    esp,0x14
0x00401391 <+5>:     mov    esi,DWORD PTR [esp+0x20] ; (1)
0x00401395 <+9>:     test   esi,esi
0x00401397 <+11>:    je     0x4013b1 <loop+37>
0x00401399 <+13>:    xor    ebx,ebx                  ; (2)
0x0040139b <+15>:    nop
0x0040139c <+16>:    mov    DWORD PTR [esp+0x4],ebx
0x004013a0 <+20>:    mov    DWORD PTR [esp],0x403064
0x004013a7 <+27>:    call   0x401b0c <printf>
0x004013ac <+32>:    inc    ebx
0x004013ad <+33>:    cmp    ebx,esi
0x004013af <+35>:    jne    0x40139c <loop+16>
0x004013b1 <+37>:    add    esp,0x14
0x004013b4 <+40>:    pop    ebx
0x004013b5 <+41>:    pop    esi
0x004013b6 <+42>:    ret
0x004013b7 <+43>:    nop
  1. zいくつかの未使用の registeresiになります。レジスタは最速のストレージ クラスです。
  2. の値を格納するためにi使用されるコンパイラも登録します。ebxi

結局のところ、使用するコンパイラと最適化オプションに依存しますが、すべての場合において、C は VB よりもはるかに高速です。

于 2013-01-31T14:06:58.360 に答える
0

コンパイラによって異なりますが、SlowVariable の値が一定の場合、最新のコンパイラのほとんどが最適化してくれると思います。

于 2013-01-31T12:35:01.800 に答える
0

「それ」(言語)は言いません。もちろん、変数が毎回評価されるかのように動作する必要があります。

最適化コンパイラは多くの巧妙な処理を実行できるため、この種のマイクロ最適化は常にコンパイラに任せるのが最善です。

手動で最適化を行う場合は、必ずプロファイリング(=測定) し、生成されたコードを読んでください。

于 2013-01-31T12:35:23.547 に答える
0

実際には、「SlowVariable」とコンパイラの動作に依存します。あなたのスロー変数が volatile の場合、キーワード volatile が許可しないため、コンパイラはそれをキャッシュする努力をしません。「揮発性」でない場合、コンパイラーがこの変数を一度レジスターにロードすることで、この変数への連続アクセスを最適化する可能性が高くなります。

于 2013-01-31T12:36:03.397 に答える