Как скомпилировать коммутатор в Visual С++ и насколько он оптимизирован и быстро?

Как я выяснил, я могу использовать только числовые значения в операторах С++ switch, я думал, что там должно быть какое-то более глубокое различие между ним и связью if-else.

Поэтому я спросил себя:

  • (Как) отличается switch от if-elseif-elseif с точки зрения скорости выполнения, оптимизации времени компиляции и общей компиляции? Я в основном говорю о MSVC здесь.

Ответ 1

Переключатель часто компилируется в таблицу перехода (одно сравнение, чтобы выяснить, какой код запускать), или если это невозможно, компилятор может по-прежнему изменять порядок сравнений, чтобы выполнить двоичный поиск среди значений (сопоставление log N). Цепочка if-else представляет собой линейный поиск (хотя, я полагаю, если все соответствующие значения являются интегральными константами времени компиляции, компилятор мог бы в принципе выполнить аналогичные оптимизации).

Ответ 2

Операторы Switch часто являются общим источником оптимизации компилятора. То есть, как они обрабатываются, зависит от настроек оптимизации, которые вы используете в своем компиляторе.

Самый простой (не оптимизированный) способ компиляции оператора switch - рассматривать его как цепочку операторов if ... else if .... Общий способ, которым компиляторы оптимизируют коммутатор, - это преобразовать его в таблицу перехода , которая может выглядеть примерно так:

if (condition1) goto label1;
if (condition2) goto label2;
if (condition3) goto label3;
else            goto default;
label1:
  <<<code from first `case statement`>>>
  goto end;
label2:
  <<<code from first `case statement`>>>
  goto end;
label3:
  <<<code from first `case statement`>>>
  goto end;
default:
  <<<code from `default` case>>>
  goto end;
end:

Одна из причин, по которой этот метод работает быстрее, состоит в том, что код внутри условных выражений меньше (поэтому существует меньшее ограничение на кеширование команд, если условное значение неверно предсказано). Кроме того, "падающий" случай становится более тривиальным для реализации (компилятор не выполняет инструкцию goto end).

Компиляторы могут дополнительно оптимизировать таблицу перехода, создав массив указателей (в местах, помеченных метками), и используйте значение, которое вы включаете в качестве индекса в этот массив. Это устранит почти все условные коды из кода (за исключением того, что необходимо для проверки того, совпадает ли значение, которое вы используете, с одним из ваших случаев).

Предупреждение: вложенные таблицы перехода трудно сгенерировать, а некоторые компиляторы отказываются даже пытаться их создать. По этой причине избегайте вложения switch внутри другого switch, если для вас важен максимально оптимизированный код (я не уверен на 100%, как MSVC, в частности, обрабатывает вложенные switch es, но руководство по компиляции должно сказать вам).