На самом низком уровне (в аппаратном обеспечении), да, , если с дороги. Чтобы понять почему, вы должны понять, как работают конвейеры .
Текущая команда, которая должна быть выполнена, хранится в чем-то, что обычно называется указатель инструкции (IP) или программный счетчик (ПК); эти термины являются синонимами, но разные термины используются с разными архитектурами. Для большинства инструкций ПК следующей инструкции - это просто текущий ПК плюс длина текущей инструкции. Для большинства архитектур RISC все инструкции имеют постоянную длину, поэтому ПК можно увеличивать на постоянную величину. Для архитектур CISC, таких как x86, инструкции могут иметь переменную длину, поэтому логика, которая декодирует инструкцию, должна выяснить, как долго текущая команда должна найти местоположение следующей инструкции.
Для команд ответвление , однако, следующая выполняемая инструкция не является следующей ячейкой после текущей инструкции. Ветви - это gotos - они сообщают процессору, где находится следующая инструкция. Ветви могут быть условными или безусловными, а целевое местоположение может быть либо фиксированным, либо вычисленным.
Условное и безусловное легко понять - условная ветвь берется только в том случае, если выполняется определенное условие (например, равно ли одно число другому); если ветвление не занято, управление переходит к следующей инструкции после ветвления, как обычно. Для безусловных ветвей ветка всегда берется. Условные ветви отображаются в операторах if
и контрольных тестах циклов for
и while
. Безусловные ветви отображаются в бесконечных циклах, вызовах функций, возвратах функций, операторах break
и continue
, печально известном операторе goto
и многих других (эти списки далеко не исчерпывающие).
Цель ветки - еще одна важная проблема. Большинство ветвей имеют фиксированную цель ветки - они идут в определенное место в коде, который фиксируется во время компиляции. Это включает в себя if
операторы, циклы всех видов, регулярные вызовы функций и многое другое. Вычисленные ветви вычисляют цель ветви во время выполнения. Это включает в себя switch
операторы (иногда), возврат из функции, вызовы виртуальных функций и вызовы указателей функций.
Так что же все это значит для производительности? Когда процессор видит, что инструкция перехода появляется в его конвейере, он должен выяснить, как продолжать заполнять свой конвейер. Чтобы выяснить, какие инструкции следуют после ветви в потоке программы, необходимо знать две вещи: (1) будет ли выбрана ветвь и (2) цель ветвления. Понимание этого называется предсказанием ветвления , и это сложная проблема. Если процессор угадает правильно, программа продолжается на полной скорости. Если вместо этого процессор угадывает неправильно , он просто потратил некоторое время, вычисляя неправильную вещь. Теперь он должен очистить свой конвейер и перезагрузить его с инструкциями из правильного пути выполнения. Итог: большой успех производительности.
Таким образом, причина, по которой заявления являются дорогостоящими, связана с ошибочными прогнозами в ветвях . Это только на самом низком уровне. Если вы пишете код высокого уровня, вам не нужно беспокоиться об этих деталях. Вы должны заботиться об этом, только если вы пишете чрезвычайно критичный для производительности код на C или сборке. Если это так, то написание кода без ответвлений часто может превосходить код с ответвлением, даже если требуется еще несколько инструкций. Есть несколько интересных трюков, которые вы можете использовать для вычисления таких вещей, как abs()
, min()
и max()
без ветвления.