を、GCCは、if文で条件文が真となることを前提と - そこに例外はありますが、それらは、文脈上の
g(int*):
testq %rdi, %rdi
je .L6
jmp t(int*)
.L6:
xorl %eax, %eax
ret
(godbolt参照)
を:
extern int t(int*);
int g(int* ip) {
if (!ip)
return 0;
return t(ip);
}
を生成しながら
extern int s(int);
int f(int i) {
if (i == 0)
return 1;
return s(i);
}
は
f(int):
testl %edi, %edi
jne .L4
movl $1, %eax
ret
.L4:
jmp s(int)
を生成します
f
でブランチがjne
(条件が真と仮定する)であるのに対して、g
では条件は偽であるとみなされます。
は今、次のと比較:
我々は文脈要因のいずれかを参照してくださいここで
x(int, int*):
testq %rsi, %rsi
je .L3 # first branch: assumed unlikely
movl $2, %eax
testl %edi, %edi
jne .L12 # second branch: assumed likely
ret
.L12:
pushq %rbx
movq %rsi, %rbx
call s(int)
movl %eax, %edx
movl $3, %eax
testl %edx, %edx
je .L13 # third branch: assumed likely
.L2:
popq %rbx
ret
.L3:
movl $1, %eax
ret
.L13:
movq %rbx, %rdi
call t(int*)
movl %eax, %edx
movl $4, %eax
testl %edx, %edx
jne .L2 # fourth branch: assumed unlikely!
movq %rbx, %rdi
call t(int*)
popq %rbx
movl %eax, %edi
jmp s(int)
を生成
extern int s(int);
extern int t(int*);
int x(int i, int* ip) {
if (!ip)
return 1;
if (!i)
return 2;
if (s(i))
return 3;
if (t(ip))
return 4;
return s(t(ip));
}
:GCCは、それがここにL2
を再利用することができることを発見し、それをすることを決めたように最終的な条件付きではないと判断し、コードを少なくすることができます。
は、あなたが与えた例の組立見てみましょう:
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
extern void raiseError();
int f(int A, int B, int Z)
{
if (A)
return 1;
else if (B)
return 2;
else if (Z)
return 3;
raiseError();
return -1;
}
アセンブリlooks like this:!Zが真であるとき、それはすでにかのように振る舞うます
f(int, int, int):
movl $1, %eax
testl %edi, %edi
jne .L9
movl $2, %eax
testl %esi, %esi
je .L11
.L9:
ret
.L11:
testl %edx, %edx
je .L12 # branch if !Z
movl $3, %eax
ret
.L12:
subq $8, %rsp
call raiseError()
movl $-1, %eax
addq $8, %rsp
ret
注生成されたコード分岐することをZは可能性が高い。 Zがそうであると言えばどうなりますか?
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
extern void raiseError();
int f(int A, int B, int Z)
{
if (A)
return 1;
else if (B)
return 2;
else if (likely(Z))
return 3;
raiseError();
return -1;
}
今we get
f(int, int, int):
movl $1, %eax
testl %edi, %edi
jne .L9
movl $2, %eax
testl %esi, %esi
je .L11
.L9:
ret
.L11:
movl $3, %eax # assume Z
testl %edx, %edx
jne .L9 # but branch if Z
subq $8, %rsp
call raiseError()
movl $-1, %eax
addq $8, %rsp
ret
ここでのポイントは、あなたがこれらのマクロを使用するときに慎重に、あなたが期待していた結果を得ることを確認する前に、慎重に後にコードを調べなければならないことである、とベンチマーク(例えばperfで)、プロセッサが生成しているコードと一致する予測をしていることを確認します。
確かに見つかる唯一の方法は、(最適化を有効にして)ビルドし、生成されたコードをチェックすることです。使用される可能性のあるものと無いものとで生成されたコードを比較する。 –
他のすべての条件の周りに '恐らく'を置かないのはなぜですか? –
@KerrekSBそれは私が避けたいものです。条件のすべてが真ではないという条件を除いて、すべての条件が同様に高い可能性があります。 – Leo