それは、少なくとも1つのC++コンパイラは(0はコンパイル時にわかっている場合)の状況を認識し、それノーオペレーションになりますよう特定です:
ソース
inline int shift(int what, int bitcount)
{
return what >> bitcount ;
}
int f() {
return shift(42,0);
}
コンパイラスイッチ
icpc -S -O3 -mssse3 -fp-model fast=2 bitsh.C
インテルC++ 11。あなたが..B1.1で見ることができるように0アセンブリ
# -- Begin _Z1fv
# mark_begin;
.align 16,0x90
.globl _Z1fv
_Z1fv:
..B1.1: # Preds ..B1.0
movl $42, %eax #7.10
ret #7.10
.align 16,0x90
# LOE
# mark_end;
.type _Z1fv,@function
.size _Z1fv,.-_Z1fv
.data
# -- End _Z1fv
.data
.section .note.GNU-stack, ""
# End
、Intelは "42を返す" ために "シフト(42.0)を返す" コンパイル
インテル11は、これらの2つのバリエーションのためのシフトに選び出す:シフト値は、コンパイル時に不可知である場合には
int g() {
int a = 5;
int b = 5;
return shift(42,a-b);
}
int h(int k) {
return shift(42,k*0);
}
...
int egad(int m, int n) {
return shift(42,m-n);
}
を...シフトはできません
# -- Begin _Z4egadii
# mark_begin;
.align 16,0x90
.globl _Z4egadii
_Z4egadii:
# parameter 1: 4 + %esp
# parameter 2: 8 + %esp
..B1.1: # Preds ..B1.0
movl 4(%esp), %ecx #20.5
subl 8(%esp), %ecx #21.21
movl $42, %eax #21.10
shrl %cl, %eax #21.10
ret #21.10
.align 16,0x90
# LOE
# mark_end;
...しかし、少なくともインラインでコールオーバーヘッドはないので、避けてください。
ボーナスアセンブリ:揮発性が高価です。あなたは値があなたがプッシュするマシン上で作業しているので、もしソースが...代わりに無操作の
int g() {
int a = 5;
volatile int b = 5;
return shift(42,a-b);
}
は...、...
..B3.1: # Preds ..B3.0
pushl %esi #10.9
movl $5, (%esp) #12.18
movl (%esp), %ecx #13.21
negl %ecx #13.21
addl $5, %ecx #13.21
movl $42, %eax #13.10
shrl %cl, %eax #13.10
popl %ecx #13.10
ret #13.10
.align 16,0x90
# LOE
# mark_end;
...にコンパイルあなたがそれらをポップしたときにスタック上の同じではないかもしれません、まあ、この欠落した最適化はおそらくあなたの問題の最小です。
これはコンパイラ固有のものであり、標準とは*関数呼び出しを作成する必要があるとは思われません。 bitCountが定数0、場合によっては0を含む変数に渡された場合、グローバル最適化は非常に簡単に関数呼び出しを呼び出す可能性があります(少なくとも変数を調べるコードがあるので、no-opではありません) 。それはコンパイラに完全に依存します。 – paxdiablo
もちろん、私は野生のコンパイラがそのレベルの最適化をしたことは一度も見たことがありません - 私は投資収益率がそれを保証するには不十分だと考えています。 ---- int(int x、int shift) { if(shift> 0){/ *インライン展開を禁止する大きなブロック(デフォルトのリリース設定): – paxdiablo
Pax、VC9は、 * /} return x << shift; } ---- 別の翻訳単位から呼び出しても、shift = 0を渡すと、呼び出しと>>が間引かれます。すごく印象的! – peterchen