Компиляторы действительно хороши в оптимизации 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, а смещение не делает все остальное, что нужно).