2016-08-18 24 views
6

ben varsa:() Preprocessor makrolar içinde if-else if zincir

#define likely(x)  __builtin_expect((x),1) 
#define unlikely(x)  __builtin_expect((x),0) 

if (A) 
    return true; 
else if (B) 
    return false; 
... 
else if (Z) 
    return true; 
else 
    //this will never really happen!!!! 
    raiseError(); 
    return false; 

Ben kapanış konuşmasında delalet muhtemeldir() else if (likely(Z)) gibi son şart çek etrafında koyabilir miyim (else) derleyicinin önceki kontrollerin şube tahminini etkilemesi çok olası değil mi?

Temel olarak, bir kestirici belirtisi olan tek bir koşullu deyim varsa, GCC tüm if-else'i optimize etmeyi dener mi?

+0

Kesin olanı öğrenmenin tek yolu (en iyileştirmeler etkinleştirilmiş) oluşturmak ve oluşturulan kodu kontrol etmektir. Kullanılma olasılığı olan ve olmayan kodları karşılaştırın. –

+0

Neden diğer tüm koşulların yanında “olası” değil? –

+0

@KerrekSB Bunu önlemek istediğim şey. Hiçbir koşulun doğru olmadığı durumlar hariç tüm şartlar eşit derecede muhtemeldir. – Leo

cevap

8

Bu açık yapacaktır:

if (A) 
    return true; 
else if (B) 
    return true; 
... 
else if (Y) 
    return true; 
else { 
    if (likely(Z)) 
    return true; 

    raiseError(); 
    return false; 
} 

Şimdi açıkça niyetini anlar derlemek ve diğer şube olasılıkları yeniden atama olmayacaktır. Ayrıca kod okunabilirliği arttı.

P.S. Ben Linux çekirdeği sessiz ayrılmaz gelen atmalarını korumak için ne şekilde ayrıca muhtemel ve olası yeniden yazmak önermek: - istisnaları vardır, ancak bunlar genelde

#define likely(x)  __builtin_expect(!!(x), 1) 
#define unlikely(x) __builtin_expect(!!(x), 0) 
2

, GCC ifadelerin doğru olacaktır eğer koşul olduğunu varsayar bağlamsal.

extern int t(int*); 
int g(int* ip) { 
    if (!ip) 
    return 0; 
    return t(ip); 
} 

üretir

f(int): 
     testl %edi, %edi 
     jne  .L4 
     movl $1, %eax 
     ret 
.L4: 
     jmp  s(int) 

ise

extern int s(int); 

int f(int i) { 
    if (i == 0) 
    return 1; 
    return s(i); 
} 

üretir:

g(int*): 
     testq %rdi, %rdi 
     je  .L6 
     jmp  t(int*) 
.L6: 
     xorl %eax, %eax 
     ret 

( godbolt bakınız)

Şubenin f şubesinin jne (koşulun doğru olduğunu varsayalım) şeklini not edin; g ise koşulun yanlış olduğu varsayılır. o karar GCC onu yeniden kullanımını ki burada L2 lekeli, bu yüzden:

Burada bağlam faktörlerden biri bkz

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) 

üretir

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)); 
} 

: Aşağıdaki ile Şimdi

karşılaştırmak Daha az kod yayabilmesi için son şartsız ihtimalleri dikkate alın. ! Oluşturulan kod dalları Z doğrudur, zaten sanki işlevi gördüğünden

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 

Not:

#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; 
} 

montaj looks like this:

Verdiğin örnek montajı inceleyelim Z muhtemeldir. Z'nin muhtemel olduğunu söylersek ne olur?

#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; 
} 

şimdi 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 

burada nokta dikkatli olmanız bu makroları kullanırken ve daha önce kodu incelemek ve dikkatli sonra emin Beklediğiniz sonuç almak ve kıyaslama yapmak gerektiğidir (örneğin, perf ile) işlemcinin, oluşturduğunuz kodla uyumlu tahminler yaptığını doğrulamak için.