Компиляторы действительно хороши в оптимизации 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
с движением/сменой. Он компилирует версию 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
вездесущ, и a compiler bug)
rep ret
используется в отрасли целей, и следующих условных ветвей, в пользу старых AMD K8 и K10 (добульдозер): What does `rep ret` mean?. Без него предсказание ветвлений не работает также и на этих устаревших процессорах.
bt
(бит тест) с регистром arg быстро. Он сочетает в себе работу сдвига влево по 1 на errNumber
бит и делает test
, но по-прежнему остается 1 задержка цикла и только один процессор Intel. Это медленное с аргументом памяти arg из-за своей семантики CISC: с операндом памяти для «битовой строки», адрес байта, который должен быть протестирован, вычисляется на основе другого arg (деленного на 8) и isn 't ограничено блоком 1, 2, 4 или 8 байтов, на который указывает операнд памяти.
От Agner Fog's instruction tables инструкция сдвига переменной счёта медленнее, чем bt
на недавнем Intel (2 раза вместо 1, а смена не делает все остальное, что необходимо).
Это отредактировано как 'субъективное'? В самом деле? Разумеется, «субъективный» - это то, что невозможно доказать так или иначе? – 2008-09-18 23:42:19
Уверен, что вы можете видеть его, с точки зрения которого генерируется наиболее эффективный код, но любой современный компилятор должен быть одинаково эффективным. В конце концов, это больше вопрос о цвете байка. – jfs 2008-09-18 23:59:09
Я не согласен, я не думаю, что это субъективно. Простая разность ASM имеет значение, вы не можете просто игнорировать несколько секунд оптимизации во многих случаях.И в этом вопросе, это не религиозная война или дебаты, есть рациональное объяснение того, почему нужно быть быстрее, просто прочитайте принятый ответ. – chakrit 2008-09-24 18:53:25