コンパイラの最適化を抑制する(GCC編) ひとりなんとかカントカー 2

性能劣化を調査したりする場合、コンパイラのチューニングによってコードが消えてしまうと困る場合はよくあります。

場合に応じて適切に対応することで、コンパイラのチューニングを抑制していきましょう。


(GCCと互換性の高いclangでも同じように対応できると思います。VC,ICC編はないです)

  • インライン展開しないで
    __attribute__((noinline,noclone))

関数に attribute noinline, noclone を付けます。今のGCCは、引数が定数だった場合などに、その定数にあわせて関数を複数生成することがあるので、nocloneも付けたほうが確実です。

static int f(int a) __attribute__((noinline,noclone));
static int f(int a) { return a; }

int main () { return f(100); } 

などで試すと、noclone の有無で出るコードが変わるのが確認できる可能性が高いです。

  • 途中の値を消さないで欲しい
__asm__ __volatile__( " # test" : : "r" (a) );

のようにasm文のオペランドに残したい値を渡します。

int f(int a) {
    a += 3;
    __asm__ __volatile__( " # test" : : "r" (a) );
    return a + 4;
}
f:
.LFB0:
	.cfi_startproc
	leal	3(%rdi), %eax
#APP
# 3 "op.c" 1
	 # test
# 0 "" 2
#NO_APP
	leal	7(%rdi), %eax
	ret
	.cfi_endproc

+3, +7 が出ます


  • メモリ読み書きを消さないで欲しい
__asm__ __volatile__( " " : : : "memory");

を入れます。

void f(int *p) {
   p[0] = 0;
   __asm__ __volatile__( " " : : : "memory");
   p[0] = 0;
}
f:
.LFB0:
	.cfi_startproc
	movl	$0, (%rdi)
#APP
# 3 "op.c" 1
	 
# 0 "" 2
#NO_APP
	movl	$0, (%rdi)
	ret


残念ながら、

void f(void) {
   int a[10];
   a[0] = 0;
   __asm__ __volatile__( " " : : : "memory");
   a[0] = 0;
}

こういうのは消えますね。

void f(void) {
   int a[10];
   a[0] = 0;
   __asm__ __volatile__( " " :: "r"(a) : "memory");
   a[0] = 0;
   __asm__ __volatile__( " " ::: "memory");

}


まあ臨機応変で…(雑な終わりかた)