Компиляторы действительно хороши в оптимизации switch. Недавние gcc также хороши для оптимизации множества условий в if.
Я сделал несколько тестов на Godbolt .
Когда caseзначения сгруппированы близко друг к другу, gcc, clang и icc достаточно умны, чтобы использовать растровое изображение, чтобы проверить, является ли значение одним из специальных.
например, gcc 5.2 -O3 компилирует в switch(и что- ifто очень похожее):
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()
Обратите внимание, что растровое изображение является непосредственными данными, поэтому нет потенциальной ошибки при обращении к кешу данных или к таблице переходов.
gcc 4.9.2 -O3 компилирует в switchрастровое изображение, но делает 1U<<errNumberс mov / shift. Компилирует ifверсию в серию веток.
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()
Обратите внимание, как он вычитает 1 из errNumber(с, leaчтобы объединить эту операцию с ходом). Это позволяет ему вписать растровое изображение в 32-битную немедленную, избегая 64-битной немедленнойmovabsq который занимает больше байтов инструкции.
Более короткая (в машинном коде) последовательность будет:
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
(Отказ от использования jc fire_special_eventвездесущ и является ошибкой компилятора .)
rep retиспользуется в целях ветвления и следующих условных ветвлениях для старых AMD K8 и K10 (pre-Bulldozer): Что означает «rep ret»? , Без этого предсказание ветвлений не работает так же хорошо на этих устаревших процессорах.
bt(битовый тест) с регистром arg быстрый. Он сочетает в себе работу сдвига влево на 1 errNumberбит и делаетtest , но все еще имеет задержку в 1 цикл и только один цикл Intel UOP. Он медленен с аргументом памяти из-за его семантики "слишком слишком CISC": с операндом памяти для "битовой строки" адрес проверяемого байта вычисляется на основе другого аргумента (деленного на 8), и isn не ограничивается 1, 2, 4 или 8-байтовым фрагментом, на который указывает операнд памяти.
Из таблиц команд Agner Fog команда сдвига с переменным счетом медленнее, чем btна недавнем Intel (2 моп вместо 1, а смещение не делает все остальное, что нужно).