コンパイルした後はコンパイラが吐き出したアセンブリコードを見る習慣を付けよと日々申しておりますが(嘘)、今回はちょっとした癖のお話。
下のプログラムと Xcode 2.4.1(powerpc-apple-darwin8-gcc-4.0.1) でコンパイルした結果。
void copy1( int bytes, const int * a, int * b )
{
int count = bytes;
count /= sizeof(int);
if ( count > 0 ) printf( "foo\n" );
for ( ; count > 0; count--, a++, b++ )
{
*b = *a;
}
}
void copy2( int bytes, const int * a, int * b )
{
int count = bytes;
if ( count > sizeof(int) ) printf( "foo\n" );
for ( count /= sizeof(int); count > 0; count--, a++, b++ )
{
*b = *a;
}
}
_copy1:
……省略……
L4:
lwz r0, 0(r30) ; r0 = *a
addi r29, r29, -1 ; count--
addi r30, r30, 4 ; a++
cmpwi cr4, r29, 0 ; cr4 = count > 0
stw r0, 0(r31) ; *b = r0
addi r31, r31, 4 ; b++
L8:
bgt cr4, L4 ; if (count > 0) goto L4
……省略……
_copy2:
L14:
lwz r0, 0(r30) ; r0 = *a
addi r30, r30, 4 ; a++
stw r0, 0(r31) ; *b = r0
addi r31, r31, 4 ; b++
L13:
bdnz L14 ; if (--count > 0) goto L14
……省略……
違いは一目瞭然。
途中の if (...) printf() のくだりは帯域的な最適化がかかりにくくなるように、わざと count の計算と for() の間に挟んだ物。
実は Xcode のデフォルトの Release の最適化オプション「最も高速で最小(-Os)」でのみ起こり、-O1 から -O3 では copy1 は copy2 と同じコードを吐く。
現実にはこのような些細な違いで速度に影響は出てこないが、日頃からコンパイラの出力を見る癖を付けておくと、こういった癖に気づいたり最適化の勉強になったりするはず。
<独り言>Visual C++ なんかは割と賢く最適化してくれるが、出力を見ているとレジスタが余っているのに極力最小限のレジスタで済まそうとしていて時にイラっと来る事も。</独り言>