Насколько компиляторы C / C ++ оптимизируют условные операторы?

Недавно я столкнулся с ситуацией, когда написал следующий код:

for(int i = 0; i < (size - 1); i++)
{
    // do whatever
}

// Assume 'size' will be constant during the duration of the for loop

Глядя на этот код, я задался вопросом, как именно оценивается условие цикла for для каждого цикла. В частности, мне любопытно, будет ли компилятор «оптимизировать» любую дополнительную арифметику, которая должна выполняться для каждого цикла. В моем случае, будет ли этот код скомпилирован так, чтобы (размер - 1) нужно было оценивать для каждой итерации цикла? Или компилятор достаточно умен, чтобы понять, что переменная size не изменится, поэтому он может предварительно вычислить ее для каждой итерации цикла.

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

В качестве примера, как компилируются следующие два фрагмента кода:

if(6)

if(1+1+1+1+1+1)

int foo = 1;
if(foo + foo + foo + foo + foo + foo)

Насколько умен компилятор? Будут ли три перечисленных выше случая преобразованы в один и тот же машинный код?

И пока я нахожусь, почему бы не привести еще один пример. Что делает компилятор, если вы выполняете операцию в условном выражении, которая не повлияет на конечный результат? Пример:

if(2*(val))

// Assume val is an int that can take on any value

В этом примере умножение совершенно не нужно. Хотя этот случай кажется намного более глупым, чем мой исходный, вопрос все еще остается: сможет ли компилятор удалить это ненужное умножение?

Вопрос:

  • Насколько оптимизированы условные операторы?
  • Это зависит от компилятора?
#
Источник
  • 2
    Сколько за оптимизацию не отвечает. Чего ждете? Процент? Оптимизация на 50%? Субъективный термин вроде «лотов»?
  • 0
    @Tas Давай, мужик, это открытый вопрос. Комментировать некоторые из перечисленных мною примеров было бы более чем достаточно.
  • 0
    Скажите вашему компилятору, чтобы он сгенерировал список на языке ассемблера. Обычно оператор сравнения или условный оператор - это 2 оператора ассемблера: сравнение и ветвление.
  • 0
    Для if (2*(val)) обратите внимание, что умножение может быть удалено, если val является int (потому что переполнение не определено), но не, если это unsigned int , потому что оно может быть завершено до 0.
  • 0
    @Tas В будущем просьбы относятся к ответам, подобным тому, что опубликовал Гэвин Хейнс. Ответ комментирует некоторые из заданных вопросов, а затем предоставляет ссылку для дальнейшего обсуждения.
  • 3
    @Teague: В будущем: (а) не кусайте руку, которая вас кормит; (б) не обучайте людей, которым вы просите помощи; (в) прислушивайтесь к заданным вопросам, потому что они заданы не для развлечения. Они созданы либо для того, чтобы искренне прояснить ваш вопрос, либо, что более вероятно, чтобы отметить очень важный момент.
Codelisting
за 2 против
Лучший ответ

Спецификация языка C ++ позволяет компилятору производить любую оптимизацию, которая не приводит к заметным изменениям ожидаемых результатов.

Если компилятор может определить, чтоsize является постоянным и не будет изменяться во время выполнения, он определенно может выполнить эту конкретную оптимизацию.

В качестве альтернативы, если компилятор также может определить, чтоi не используется в цикле (и его значение не используется впоследствии), что он используется только как счетчик, он вполне может переписать цикл следующим образом:

for(int i = 1; i < size; i++)

потому что это может привести к уменьшению кода. Даже если этоi каким-то образом используется, компилятор все еще может внести это изменение, а затем скорректировать все остальное использованиеi так что наблюдаемые результаты остаются прежними.

Подведем итог: все идет. Компилятор может или не может вносить какие-либо изменения в оптимизацию, пока наблюдаемые результаты остаются такими же.

за 4 против

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

Одно из самых больших препятствий, с которыми сталкиваются новички в языке, пытаясь по- настоящему понять C ++, - это отсутствие однозначной связи между их кодом и тем, что выполняет компьютер. Вся цель языка - создать абстракцию . Вы определяете семантику программы, но компьютер не несет ответственности за фактическое выполнение вашего кода C ++ построчно; действительно, если бы это было так, это было бы ужасно медленным по сравнению со скоростью, которую мы можем ожидать от современных компьютеров.

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

Есть исключения? Безусловно. Иногда ваши требования настолько специфичны, что вы знаете лучше, чем компилятор, и в конечном итоге оптимизируете. Обычно это делается после профилирования и определения узких мест. И также нет оправдания написанию заведомо глупого кода. В конце концов, если вы изо всех сил попросите свою программу скопировать вектор размером 50 МБ, то она скопирует вектор размером 50 МБ.

Но, если предположить, что разумный код означает то, как он выглядит, вам действительно не следует тратить слишком много времени на то, чтобы беспокоиться об этом. Современные компиляторы настолько хороши в оптимизации, что было бы глупо пытаться не отставать.

за 2 против
  • Да, оптимизации много, и это очень сложно.
  • Он зависит от компилятора, а также зависит от параметров компилятора.

Проверьте https://meta.stackexchange.com/questions/25840/can-we-stop-recommending-the-dragon-book-пожалуйста, ознакомьтесь с некоторыми рекомендациями из книг, если вы действительно хотите понять, что может делать компилятор. Это очень сложный предмет.

Вы также можете скомпилировать в сборку с помощью-S option (gcc / g ++), чтобы увидеть, что на самом деле делает компилятор. Использовать-O3 / ... /-O0 /-O поэкспериментировать с разными уровнями оптимизации.

Codelisting
Популярные категории
На заметку программисту