Compiler sangat bagus dalam mengoptimalkan switch. Gcc terbaru juga baik dalam mengoptimalkan banyak kondisi dalam if.
Saya membuat beberapa test case di godbolt .
Ketika case nilai dikelompokkan berdekatan, gcc, clang, dan icc semuanya cukup pintar untuk menggunakan bitmap untuk memeriksa apakah suatu nilai adalah salah satu yang istimewa.
misalnya gcc 5.2 -O3 mengkompilasi switchto (dan ifsesuatu yang sangat mirip):
errhandler_switch(errtype): # gcc 5.2 -O3
cmpl $32, %edi
ja .L5
movabsq $4301325442, %rax # highest set bit is bit 32 (the 33rd bit)
btq %rdi, %rax
jc .L10
.L5:
rep ret
.L10:
jmp fire_special_event()
Perhatikan bahwa bitmap adalah data langsung, jadi tidak ada potensi data-cache ketinggalan mengaksesnya, atau tabel lompatan.
gcc 4.9.2 -O3 mengkompilasi switchke bitmap, tetapi melakukan 1U<<errNumberdengan mov / shift. Ini mengkompilasi ifversi ke serangkaian cabang.
errhandler_switch(errtype): # gcc 4.9.2 -O3
leal -1(%rdi), %ecx
cmpl $31, %ecx # cmpl $32, %edi wouldn't have to wait an extra cycle for lea's output.
# However, register read ports are limited on pre-SnB Intel
ja .L5
movl $1, %eax
salq %cl, %rax # with -march=haswell, it will use BMI's shlx to avoid moving the shift count into ecx
testl $2150662721, %eax
jne .L10
.L5:
rep ret
.L10:
jmp fire_special_event()
Perhatikan cara mengurangi 1 dari errNumber(dengan leamenggabungkan operasi itu dengan gerakan). Itu memungkinkannya menyesuaikan bitmap menjadi 32bit segera, menghindari 64bit-langsungmovabsq yang membutuhkan lebih banyak byte instruksi.
Urutan yang lebih pendek (dalam kode mesin) adalah:
cmpl $32, %edi
ja .L5
mov $2150662721, %eax
dec %edi # movabsq and btq is fewer instructions / fewer Intel uops, but this saves several bytes
bt %edi, %eax
jc fire_special_event
.L5:
ret
(Kegagalan untuk menggunakan jc fire_special_eventada di mana-mana, dan merupakan bug penyusun .)
rep retdigunakan dalam target cabang, dan mengikuti cabang bersyarat, untuk kepentingan AMD K8 dan K10 lama (pra-Bulldozer): Apa arti `rep ret`? . Tanpanya, prediksi cabang tidak akan bekerja dengan baik pada CPU yang usang.
bt(uji bit) dengan register arg cepat. Ini menggabungkan pekerjaan menggeser kiri 1 demi errNumberbit dan melakukan atest , tetapi masih 1 siklus latensi dan hanya satu Intel uop. Ini lambat dengan memori arg karena caranya terlalu-semantik CISC: dengan operan memori untuk "bit string", alamat byte yang akan diuji dihitung berdasarkan argumen lain (dibagi dengan 8), dan tidak terbatas pada potongan 1, 2, 4, atau 8 byte yang ditunjukkan oleh operan memori.
Dari tabel instruksi Agner Fog, instruksi shift penghitungan variabel lebih lambat dari btpada Intel terbaru (2 uops, bukan 1, dan shift tidak melakukan semua hal lain yang diperlukan).