Trình biên dịch thực sự tốt trong việc tối ưu hóa switch. Gcc gần đây cũng tốt trong việc tối ưu hóa một loạt các điều kiện trong một if.
Tôi đã thực hiện một số trường hợp thử nghiệm trên godbolt .
Khi các casegiá trị được nhóm lại gần nhau, gcc, clang và icc đều đủ thông minh để sử dụng bitmap để kiểm tra xem giá trị có phải là một trong những giá trị đặc biệt hay không.
ví dụ: gcc 5.2 -O3 biên dịch switchthành (và ifmột cái gì đó rất giống nhau):
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()
Lưu ý rằng bitmap là dữ liệu ngay lập tức, do đó, không có bộ nhớ cache dữ liệu tiềm năng nào truy cập vào nó hoặc bảng nhảy.
gcc 4.9.2 -O3 biên dịch thành switchbitmap, nhưng thực hiện 1U<<errNumbervới Mov / shift. Nó biên dịch ifphiên bản thành một loạt các chi nhánh.
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()
Lưu ý cách nó trừ 1 từ errNumber(với leađể kết hợp thao tác đó với di chuyển). Điều đó cho phép nó phù hợp với bitmap thành 32 bit ngay lập tức, tránh ngay lập tức 64 bitmovabsq cần nhiều byte lệnh hơn.
Một chuỗi ngắn hơn (trong mã máy) sẽ là:
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
(Lỗi không sử dụng jc fire_special_eventlà ở khắp mọi nơi và là lỗi của trình biên dịch .)
rep retđược sử dụng trong các mục tiêu nhánh và theo các nhánh có điều kiện, vì lợi ích của AMD K8 và K10 cũ (tiền Bulldozer): `rep ret` nghĩa là gì? . Không có nó, dự đoán nhánh không hoạt động tốt trên các CPU lỗi thời đó.
bt(kiểm tra bit) với một thanh ghi arg là nhanh. Nó kết hợp công việc dịch chuyển trái 1 errNumberbit và thực hiện một test, nhưng vẫn là độ trễ 1 chu kỳ và chỉ một uop Intel duy nhất. Nó chậm với một bộ nhớ arg vì ngữ nghĩa quá giống CISC của nó: với toán hạng bộ nhớ cho "chuỗi bit", địa chỉ của byte được kiểm tra được tính dựa trên đối số khác (chia cho 8) và không không giới hạn ở đoạn 1, 2, 4 hoặc 8byte được chỉ ra bởi toán hạng bộ nhớ.
Từ các bảng hướng dẫn của Agner Fog, hướng dẫn thay đổi số đếm chậm hơn so với btIntel gần đây (2 u thay vì 1 và shift không làm mọi thứ khác cần thiết).