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 switch
to (dan if
sesuatu 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 switch
ke bitmap, tetapi melakukan 1U<<errNumber
dengan mov / shift. Ini mengkompilasi if
versi 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 lea
menggabungkan 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_event
ada di mana-mana, dan merupakan bug penyusun .)
rep ret
digunakan 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 errNumber
bit 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 bt
pada Intel terbaru (2 uops, bukan 1, dan shift tidak melakukan semua hal lain yang diperlukan).