2013-02-09 9 views
11

tylko pokazuje przykłady, gdzie __builtin_expect() jest umieszczony wokół całego warunku instrukcji "if".Czy mogę używać GCC __builtin_expect() z Ternary operatora w C

Zauważyłem również, że GCC nie narzeka, jeśli używam go, na przykład, z operatorem potrójnym lub dowolnym dowolnym wyrażeniem integralnym, nawet takim, który nie jest używany w kontekście rozgałęzień.

Zastanawiam się, jakie są podstawowe ograniczenia jego użytkowania.

zachowa swoje działanie, gdy stosowane w potrójnego działania jak ten:

int foo(int i) 
{ 
    return __builtin_expect(i == 7, 1) ? 100 : 200; 
} 

i co o tej sprawie:

int foo(int i) 
{ 
    return __builtin_expect(i, 7) == 7 ? 100 : 200; 
} 

A ten:

int foo(int i) 
{ 
    int j = __builtin_expect(i, 7); 
    return j == 7 ? 100 : 200; 
} 

Odpowiedz

8

It najwyraźniej działa zarówno dla potrójnych, jak i regularnych instrukcji if.

Najpierw przyjrzyjmy się trzem przykładowym kodom, z których dwa używają __builtin_expect zarówno w zwykłym, jak iw trójskładnym stylu if oraz trzecim, który w ogóle go nie używa.

builtin.c:

int main() 
{ 
    char c = getchar(); 
    const char *printVal; 
    if (__builtin_expect(c == 'c', 1)) 
    { 
     printVal = "Took expected branch!\n"; 
    } 
    else 
    { 
     printVal = "Boo!\n"; 
    } 

    printf(printVal); 
} 

ternary.c:

int main() 
{ 
    char c = getchar(); 
    const char *printVal = __builtin_expect(c == 'c', 1) 
     ? "Took expected branch!\n" 
     : "Boo!\n"; 

    printf(printVal); 
} 

nobuiltin.c:

int main() 
{ 
    char c = getchar(); 
    const char *printVal; 
    if (c == 'c') 
    { 
     printVal = "Took expected branch!\n"; 
    } 
    else 
    { 
     printVal = "Boo!\n"; 
    } 

    printf(printVal); 
} 

z wkompilowaną -O3 wszystkie trzy prowadzą do tego samego zespołu. Jednak, gdy -O jest pominięta (na GCC 4.7.2), zarówno ternary.c i builtin.c mają taką samą listę montażową (gdzie ma to znaczenie):

builtin.s:

.file "builtin.c" 
    .section .rodata 
.LC0: 
    .string "Took expected branch!\n" 
.LC1: 
    .string "Boo!\n" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    .cfi_offset 5, -8 
    movl %esp, %ebp 
    .cfi_def_cfa_register 5 
    andl $-16, %esp 
    subl $32, %esp 
    call getchar 
    movb %al, 27(%esp) 
    cmpb $99, 27(%esp) 
    sete %al 
    movzbl %al, %eax 
    testl %eax, %eax 
    je .L2 
    movl $.LC0, 28(%esp) 
    jmp .L3 
.L2: 
    movl $.LC1, 28(%esp) 
.L3: 
    movl 28(%esp), %eax 
    movl %eax, (%esp) 
    call printf 
    leave 
    .cfi_restore 5 
    .cfi_def_cfa 4, 4 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (Debian 4.7.2-4) 4.7.2" 
    .section .note.GNU-stack,"",@progbits 

ternary.s:

.file "ternary.c" 
    .section .rodata 
.LC0: 
    .string "Took expected branch!\n" 
.LC1: 
    .string "Boo!\n" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    .cfi_offset 5, -8 
    movl %esp, %ebp 
    .cfi_def_cfa_register 5 
    andl $-16, %esp 
    subl $32, %esp 
    call getchar 
    movb %al, 31(%esp) 
    cmpb $99, 31(%esp) 
    sete %al 
    movzbl %al, %eax 
    testl %eax, %eax 
    je .L2 
    movl $.LC0, %eax 
    jmp .L3 
.L2: 
    movl $.LC1, %eax 
.L3: 
    movl %eax, 24(%esp) 
    movl 24(%esp), %eax 
    movl %eax, (%esp) 
    call printf 
    leave 
    .cfi_restore 5 
    .cfi_def_cfa 4, 4 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (Debian 4.7.2-4) 4.7.2" 
    .section .note.GNU-stack,"",@progbits 

Zważywszy nobuiltin.c nie:

.file "nobuiltin.c" 
    .section .rodata 
.LC0: 
    .string "Took expected branch!\n" 
.LC1: 
    .string "Boo!\n" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    .cfi_offset 5, -8 
    movl %esp, %ebp 
    .cfi_def_cfa_register 5 
    andl $-16, %esp 
    subl $32, %esp 
    call getchar 
    movb %al, 27(%esp) 
    cmpb $99, 27(%esp) 
    jne .L2 
    movl $.LC0, 28(%esp) 
    jmp .L3 
.L2: 
    movl $.LC1, 28(%esp) 
.L3: 
    movl 28(%esp), %eax 
    movl %eax, (%esp) 
    call printf 
    leave 
    .cfi_restore 5 
    .cfi_def_cfa 4, 4 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (Debian 4.7.2-4) 4.7.2" 
    .section .note.GNU-stack,"",@progbits 

Właściwa część:

diff

Zasadniczo __builtin_expect przyczyny kod dodatkowy (sete %al ...) mają być wykonane przed je .L2 opartego na wynikach testl %eax, %eax który procesor jest bardziej prawdopodobne, aby przewidzieć jako 1 (naiwne założenie, tutaj) zamiast na podstawie bezpośredniego porównania znaku wejściowego z 'c'. Podczas gdy w przypadku nobuiltin.c żaden taki kod nie istnieje i bezpośrednio po porównaniu z "c"() ().Pamiętaj, prognozy rozgałęzień są wykonywane głównie w CPU, a tutaj GCC to po prostu "układanie pułapki" dla predyktora gałęzi CPU, aby założyć, która ścieżka zostanie pobrana (za pomocą dodatkowego kodu i przełączania je i jne, chociaż nie miej to źródło, ponieważ Intel official optimization manual nie wspomina o pierwszym spotkaniu z je vs jne inaczej dla prognozy rozgałęzień! Mogę tylko założyć, że zespół GCC doszedł do tego przez próbę i błąd).

Jestem pewien, że istnieją lepsze przypadki testowe, w których prognoza rozgałęzień GCC może być widziana bardziej bezpośrednio (zamiast obserwować podpowiedzi do procesora), chociaż nie wiem, jak naśladować taki przypadek zwięźle/zwięźle. (Zgadnij: prawdopodobnie będzie to wymagało rozwijania pętli podczas kompilacji.)

+0

Bardzo ładna analiza i bardzo przyjemna prezentacja wyników. Dziękuję za trud. –

+1

To naprawdę nie pokazuje niczego poza tym, że '__builtin_expect' nie ma wpływu na zoptymalizowany kod dla x86 (ponieważ powiedziałeś, że były takie same dla -O3). Jedynym powodem, dla którego różnią się one wcześniej, jest to, że '__builtin_expect' to funkcja zwracająca wartość, która została mu podana, a ta wartość zwracana nie może się zdarzyć za pośrednictwem flag. W przeciwnym razie różnica pozostanie w zoptymalizowanym kodzie. – ughoavgfhw

+0

@ughoavgfhw: Co rozumiesz przez "że wartość zwrotu nie może się zdarzyć przez flagi"? –

Powiązane problemy