次のようなループがある場合:
for (int i = 0; i < SlowVariable; i++)
{
//
}
VB6 ではSlowVariable
、ループの反復ごとにアクセスされるため、次の処理がはるかに効率的になります。
int cnt = SlowVariable;
for (int i = 0; i < cnt; i++)
{
//
}
GCC で同じ最適化を行う必要がありますか? それともSlowVariable
一度だけ評価しますか?
次のようなループがある場合:
for (int i = 0; i < SlowVariable; i++)
{
//
}
VB6 ではSlowVariable
、ループの反復ごとにアクセスされるため、次の処理がはるかに効率的になります。
int cnt = SlowVariable;
for (int i = 0; i < cnt; i++)
{
//
}
GCC で同じ最適化を行う必要がありますか? それともSlowVariable
一度だけ評価しますか?
一般に、変な種類のメモリにマップされていない限り、変数は遅く (または速く) なりませんvolatile
(この場合、変数を宣言する必要があります)。
しかし、実際には、ローカル変数を使用すると最適化の機会が増え、その効果は非常に目に見える可能性があります。コンパイラは、ループ内で呼び出された関数がそのグローバル変数を読み書きできないことを証明できる場合にのみ、それ自体でグローバル変数を「キャッシュ」できます。ループ内で外部関数を呼び出す場合、コンパイラはおそらくそのようなことを証明できません。
これは、コンパイラがどのように最適化するかによって異なります。たとえば、次のようになります。
#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
i
スタックから にムービー化されeax
ます。eax
orを、スタックにある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
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
z
いくつかの未使用の registeresi
になります。レジスタは最速のストレージ クラスです。i
使用されるコンパイラも登録します。ebx
i
結局のところ、使用するコンパイラと最適化オプションに依存しますが、すべての場合において、C は VB よりもはるかに高速です。
コンパイラによって異なりますが、SlowVariable の値が一定の場合、最新のコンパイラのほとんどが最適化してくれると思います。
「それ」(言語)は言いません。もちろん、変数が毎回評価されるかのように動作する必要があります。
最適化コンパイラは多くの巧妙な処理を実行できるため、この種のマイクロ最適化は常にコンパイラに任せるのが最善です。
手動で最適化を行う場合は、必ずプロファイリング(=測定) し、生成されたコードを読んでください。
実際には、「SlowVariable」とコンパイラの動作に依存します。あなたのスロー変数が volatile の場合、キーワード volatile が許可しないため、コンパイラはそれをキャッシュする努力をしません。「揮発性」でない場合、コンパイラーがこの変数を一度レジスターにロードすることで、この変数への連続アクセスを最適化する可能性が高くなります。