Несмотря на то, что вы спрашиваете только о mod
против branch
, в зависимости от фактической реализации mod
и ответвления, вероятно, более чем пять случаев:
На основе модуля
Power-of-two
Если значение SIZE
известно компилятору и равно степени 2, mod
скомпилируется в один and
, как это и будет очень эффективным по производительности и размеру кода.and
по-прежнему является частью цепочки зависимостей приращения цикла, хотя ограничение скорости накладывается на производительность, равную 2 циклам на итерацию, если только компилятор не достаточно умен, чтобы развернуть его и не дать and
выйти изпереносимая цепь (gcc и clang не были).
Известно, но не как двойная степень
С другой стороны, если значение SIZE
известно, но не является степеньюиз двух, вы, вероятно, получите основанную на умножении реализацию фиксированного значения модуля, , например, .Обычно это занимает что-то вроде 4-6 инструкций, которые заканчиваются частью цепочки зависимостей.Таким образом, это ограничит вашу производительность чем-то вроде 1 итерации каждые 5-8 циклов, точно в зависимости от задержки цепочки зависимостей.
Неизвестно
В вашем примере SIZE
- известная константа, но в более общем случае, когда он не известен во время компиляции, вы получите инструкцию деления на платформах, которые его поддерживают.Что-то вроде как это .
Это хорошо для размера кода, так как это одна инструкция, но, вероятно, катастрофически для производительности, потому что теперь у вас есть медленная инструкция деления как часть переносимой зависимости дляпетля.В зависимости от вашего оборудования и типа переменной SIZE
, вы смотрите 20-100 циклов на итерацию.
На основе ветвления
Вы добавляете ветку в свой код, но переходитекомпилятор решил реализовать это как условный переход или как условный переход.В -O2
, gcc выбирает прыжок и звенит условным ходом .
Условный переход
Это прямая интерпретация вашего кода: используйте условную ветвьдля реализации условия i == SIZE
.
Преимущество состоит в том, что условие становится зависимостью управления, а не зависимостью от данных, поэтому ваш цикл будет в основном работать на полной скорости, когда ветвь не берется.
Однако производительность может серьезно пострадать, если ветка часто ошибается.Это сильно зависит от значения SIZE
и от вашего оборудования.Современный Intel должен иметь возможность прогнозировать вложенные циклы до 20 с чем-то итераций, но помимо этого он будет ошибочно прогнозировать каждый раз при выходе из внутреннего цикла.Конечно, если SIZE
очень велико, то единственный неверный прогноз в любом случае не будет иметь большого значения, поэтому наихудший случай - SIZE
, достаточно большой, чтобы неверно прогнозировать.
Условное движение
clangиспользует условный ход для обновления i
.Это разумный вариант, но он означает переносимую зависимость потока данных в 3-4 цикла.
1 Либо на самом деле константа, как в вашем примереили фактически постоянная из-за встраивания и постоянного распространения.