コンパイラの最適化を抑制する(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"); }
まあ臨機応変で…(雑な終わりかた)