Должны ли мы по-прежнему оптимизировать "в малом"?

Я менял цикл for для увеличения, используя ++i вместо i++ и додумался, это действительно необходимо больше? Конечно, сегодня компиляторы делают эту оптимизацию самостоятельно.

В этой статье http://leto.net/docs/C-optimization.php, с 1997 года Майкл Ли переходит в другие оптимизации, такие как inlining, циклическое разворачивание, задержка цикла, инверсия цикла, снижение силы и многие другие. Насколько они актуальны?

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

Изменить: это не имеет ничего общего с преждевременной оптимизацией. Решение об оптимизации уже принято. Теперь вопрос заключается в том, что является наиболее эффективным способом сделать это.

anecdote: Я однажды рассмотрел спецификацию требований, в которой говорилось: "Программист должен сдвинуться на единицу вместо умножения на 2".

Ответ 1

Если для оптимизации нет затрат, сделайте это. При написании кода ++i так же легко писать, как i++, поэтому предпочитайте первый. Для этого нет никаких затрат.

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

Но да, это может изменить ситуацию. По встроенным типам, возможно, нет, но для сложных классов компилятор вряд ли сможет его оптимизировать. Причина этого в том, что операция increment no больше не является внутренней операцией, встроенной в компилятор, а функцией, определенной в классе. Компилятор может оптимизировать его, как любую другую функцию, но не может, в общем, предположить, что предварительный приращение можно использовать вместо пост-приращения. Две функции могут делать совершенно разные вещи.

Таким образом, при определении того, какие оптимизации могут быть выполнены компилятором, рассмотрите, есть ли у него достаточно информации для его выполнения. В этом случае компилятор не знает, что post-increment и pre-increment выполняют одни и те же модификации объекта, поэтому он не может предположить, что его можно заменить другим. Но у вас есть это знание, поэтому вы можете безопасно выполнять оптимизацию.

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

Loop unrolling - чисто механическая операция, и компилятор может сделать это легко. То же самое касается сокращения силы. Переключение внутренних и внешних циклов сложнее, потому что компилятор должен доказать, что измененный порядок обхода не повлияет на результат, что трудно сделать автоматически. Итак, вот оптимизация, которую вы должны сделать самостоятельно.

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

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

Таким образом, такие "мелкомасштабные" оптимизации могут быть необходимы, если 1) вам действительно нужна производительность, и 2) у вас есть информация, которую нет в компиляторе.

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

Ответ 2

Это измученный предмет, и SO содержит куча хороших и плохих советов.

Позвольте мне рассказать вам, что я нашел из большого опыта в настройке производительности.

Существуют кривые компромисса между производительностью и другими вещами, такими как память и ясность, не так ли? И вы ожидаете, что для повышения производительности вам придется что-то придумать, правильно?

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

Используемый метод - это не измерение, это диагностика. Меня не волнует, насколько быстро выполняются различные процедуры или как часто они называются. Я хочу точно знать, какие инструкции вызывают медлительность и почему.

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

Итак, я занимаюсь одной проблемой одновременно. Я называю эти "слизняки", что сокращается для "медлительных ошибок". Каждый удаляемый ящик дает ускорение в любом месте от 1.1x до 10x, в зависимости от того, насколько он плох. Каждый удаляемый слизняк делает оставшиеся занимают большую часть оставшегося времени, поэтому их легче найти. Таким образом, все "низко висящие фрукты" могут быть быстро удалены.

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

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

ДОБАВЛЕНО: теперь, чтобы попытаться ответить на ваш вопрос, оптимизация низкого уровня должна выполняться, когда диагноз говорит о том, что у вас есть "горячая точка" (т.е. некоторый код в нижней части стека вызовов отображается на достаточном количестве образцов стека вызовов (10% или более), которые, как известно, стоят значительного времени). И если горячая точка находится в коде, вы можете редактировать ее. Если у вас есть "горячая точка" в "новом", "удалении" или сравнении строк, посмотрите вверх по стеку, чтобы вещи могли избавиться.

Надеюсь, что это поможет.

Ответ 3

Эти оптимизации по-прежнему актуальны. Что касается вашего примера, использование ++ я или я ++ по встроенному арифметическому типу не имеет эффекта.

В случае пользовательских операторов increment/decment ++ я предпочтительнее, потому что это не означает, что нужно копировать увеличиваемый объект.

Таким образом, хороший стиль кодирования заключается в использовании префикса increment/decment in для циклов.

Ответ 4

В общем, нет. Компиляторы намного лучше делают небольшие, простые микро-оптимизации, подобные этому, на всей вашей кодовой базе. Убедитесь, что вы включили свой компилятор здесь, путем компиляции вашей версии выпуска с правильными флагами оптимизации. Если вы используете Visual Studio, вы можете поэкспериментировать с предпочтением размера по скорости (есть много случаев, когда маленький код быстрее), генерация кода времени (LTCG, которая позволяет компилятору выполнять кросс-компиляционные оптимизации), и, возможно, даже оптимизацию на основе профиля.

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

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

В качестве другого плаката здесь упоминалось: "оптимизация без измерения и понимания вовсе не оптимизация - просто случайное изменение".

Если вы измерили и определили, что конкретная функция или цикл является точкой доступа, есть два подхода к ее оптимизации:

  • Сначала оптимизируйте его на более высоком уровне, уменьшив количество вызовов дорогого кода. Это, как правило, приводит к наибольшей выгоде. Улучшения уровня алгоритма попадают на этот уровень - алгоритм будет лучше, чем большой-O должен привести к сокращению кода точки доступа.
  • Если вызовы не могут быть уменьшены, вы должны учитывать микро-оптимизации. Посмотрите на фактический машинный код, который испускает компилятор, и определите, что он делает, что является наиболее дорогостоящим - если окажется, что происходит копирование временных объектов, тогда рассмотрим префикc++ поверх постфикса. Если он делает ненужное сравнение в начале цикла, переверните цикл в do/while и так далее. Не понимая, почему код медленный, любые простые микрооптимизации рядом с бесполезными.

Ответ 5

Да, эти вещи по-прежнему актуальны. Я делаю справедливую часть такой оптимизации, но, если быть честным, я в основном пишу код, который должен делать относительно сложные вещи примерно за 10 мс на ARM9. Если вы пишете код, который работает на более современных процессорах, тогда преимущества не будут такими большими.

Если вы не заботитесь о переносимости, и вы делаете справедливую бит математики, вы также можете посмотреть, как использовать любые векторные операции на вашей целевой платформе - SSE на x86, Altivec на PPC. Компиляторы не могут легко использовать эти инструкции без большой помощи, и встроенные функции довольно просты в использовании в эти дни. Еще одна вещь, которая не упоминается в документе, к которому вы привязаны, - это сглаживание указателя. Иногда вы можете получить хорошие улучшения скорости, если ваш компилятор поддерживает какое-то ключевое слово "ограничить". Плюс, конечно, важно думать о том, как использовать кеш. Реорганизация вашего кода и данных таким образом, который эффективно использует кеш, может привести к резкому увеличению скорости по сравнению с оптимизацией нечетной копии или разворачиванием цикла.

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

Ответ 6

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

Тем не менее, я считаю очень важным знать , в какой степени целевой компилятор способен оптимизировать небольшие фрагменты кода. Истина такова: современные компиляторы (иногда удивительно!) Хороши в этом. Джейсон имеет невероятную историю относительно оптимизированной (не хвостовой рекурсивной) факториальной функции.

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

И, как говорили другие, эти мельчайшие детали все еще актуальны и всегда будут (в обозримом будущем). Хотя компиляторы становятся все умнее все время, а машины становятся быстрее, увеличивается размер наших данных - фактически, мы теряем эту битву; во многих областях объем данных растет намного быстрее, чем компьютеры становятся лучше.

Ответ 7

Все перечисленные вами оптимизации в наши дни практически неактуальны для программистов C - компилятор намного лучше выполняет такие функции, как inlining, циклическое разворачивание, задержка цикла, инверсия цикла и снижение прочности.

Относительно ++i по сравнению с i++: для целых чисел они генерируют идентичный машинный код, поэтому тот, который вы используете, зависит от стиля/предпочтения. В С++ объекты могут перегружать те операторы pre- и postincrement, и в этом случае обычно предпочтительнее использовать преинкремент, потому что постинктумент требует дополнительной копии объекта.

Что касается использования сдвигов вместо умножений по степеням 2, опять же, компилятор уже делает это для вас. В зависимости от архитектуры он может делать еще более умные вещи, например, превращение умножения на 5 в одну инструкцию lea на x86. Однако с делениями и модулями по степеням 2 вам может потребоваться уделять немного больше внимания, чтобы получить оптимальный код. Предположим, вы пишете:

x = y / 2;

Если x и y являются целыми целыми числами, компилятор не может превратить это в сдвиг вправо, потому что это приведет к ошибочному результату для отрицательных чисел. Таким образом, он испускает правую смену и несколько инструкций, чтобы убедиться, что результат верен как для положительных, так и для отрицательных чисел. Если вы знаете, что x и y всегда положительны, вы должны помочь компилятору и вместо них сделать их беззнаковыми целыми числами. Затем компилятор может оптимизировать его в одну команду правого сдвига.

Оператор модуля % работает аналогично - если вы modding с силой 2, с целыми целыми числами, компилятор должен испустить команду and плюс немного больше бит, чтобы сделать результат правильным для положительного и отрицательные числа, но он может испускать одну инструкцию and, если она имеет дело с неподписанными числами.

Ответ 8

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

Пойдите зеленый, сохраните планету, оптимизируйте себя

Ответ 9

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

Ответ 10

Сделайте это правильно, затем сделайте это быстро - на основе измерения производительности.

Выбирайте алгоритмы хорошо и реализуйте их в MOST READABLE. Сократите удобочитаемость для производительности только тогда, когда вы ДОЛЖНЫ - то есть, когда ваш пользователь говорит, что производительность неприемлема ни в словах, ни в их действиях.

Как сказал Дональд Кнут/Тони Хоар, "преждевременная оптимизация - это корень всего зла" - все еще верно сейчас 30 лет спустя...

Ответ 11

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

Для целых чисел и т.д. компилятор будет генерировать идентичный код.

Ответ 12

Интересное наблюдение, которое я наблюдал за эти годы, состоит в том, что оптимизированный код одного поколения назад, по-видимому, фактически оптимизирован в следующем поколении. Это связано с тем, что изменения в процессорах меняются так, что if/else становится узким местом в современных процессорах, где трубопроводы являются глубокими. Я бы сказал, что чистый, короткий, сжатый код часто является лучшим конечным результатом. Там, где оптимизация действительно учитывается, в структурах данных, чтобы получить их правильные и тонкие.

Ответ 13

Есть три цитаты, которые, как я считаю, каждый разработчик должен знать в отношении оптимизации - я сначала прочитал их в книге Джоша Блоха "Эффективная Ява":

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

(Уильям А. Вульф)

Мы должны забыть о небольших эффективности, скажем, около 97% время: преждевременная оптимизация - это корень всего зла.

(Дональд Э. Кнут)

Мы следуем двум правилам в оптимизация:

Правило 1: Не делайте этого.

Правило 2: (только для экспертов). Не делай это еще - то есть, пока у вас нет совершенно ясный и неоптимизированный Решение.

(М. А. Джексон)

Все эти цитаты (AFAIK) не менее 20-30 лет, время, когда CPU и память означали гораздо больше, чем сегодня. Я считаю, что это правильный способ разработки программного обеспечения, прежде всего, иметь рабочее решение, а затем использовать профилировщик, чтобы проверить, где узкие места производительности. Однажды друг рассказал мне о приложении, написанном на С++ и Delphi, и имел проблемы с производительностью. Используя профилировщик, они обнаружили, что приложение потратило значительное количество времени на преобразование строк из структуры Delphi в С++, и наоборот - никакая микро-оптимизация не может обнаружить, что...

В заключение не думайте, что вы знаете, где будут проблемы с производительностью. Используйте профайлер для этого.

Ответ 14

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

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

Ответ 15

Также необходимо быть осторожным, чтобы изменение от операторов pre/post-increment/decment не приводило к нежелательному побочному эффекту. Например, если вы повторяете цикл в 5 раз просто для запуска набора кода несколько раз без какого-либо интереса к значению индекса цикла, вы, вероятно, все в порядке (YMMV). С другой стороны, если вы получаете доступ к значению индекса цикла, то результат может быть не таким, как вы ожидаете:

#include <iostream>

int main()
{
  for (unsigned int i = 5; i != 0; i--)
    std::cout << i << std::endl;

  for (unsigned int i = 5; i != 0; --i)
    std::cout << "\t" << i << std::endl;

  for (unsigned int i = 5; i-- != 0; )
    std::cout << i << std::endl;

  for (unsigned int i = 5; --i != 0; )
    std::cout << "\t" << i << std::endl;
}

приводит к следующему:

5
4
3
2
1
        5
        4
        3
        2
        1
4
3
2
1
0
        4
        3
        2
        1

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

Ответ 16

Только если вы точно знаете, что они актуальны. Это означает, что вы либо исследовали эту проблему ранее в своем конкретном компиляторе, либо уже сделали следующее:

  • созданный функциональный код
  • профилированный код
  • выявленные узкие места
  • упрощенная конструкция для устранения узких мест
  • выбранные алгоритмы, которые минимизируют количество вызовов в узких местах

Если вы сделали все это, то часто самое лучшее, что вам нужно сделать, это заставить ваш компилятор испустить что-то более низкое, которое вы можете проверить самостоятельно (например, сборку) и на основе конкретных суждений. По моему опыту каждый компилятор немного отличается. Иногда оптимизация на одном приводит к тому, что другой создает более дешевый код.

Если вы еще этого не сделали, я бы назвал это преждевременной оптимизацией, и я бы рекомендовал против нее. Оптимизация перед тем, как делать эти вещи, дает вознаграждения, которые непропорционально малы по сравнению с затратами.

Ответ 17

Я хотел кое-что добавить. Эта "преждевременная оптимизация - это плохо" - это мусор. Что вы будете делать, когда вы выберете алгоритм? Вероятно, вы берете тот, у которого лучшая временная сложность - преждевременная оптимизация OMG. Но все в порядке с этим. Так что кажется, что реальное отношение "преждевременная оптимизация - это плохо - если вы не сделаете это по-своему" В конце дня сделайте все, что вам нужно, чтобы сделать приложение, которое вам нужно сделать.

"Программист должен сдвинуться на единицу вместо умножения на 2". надеюсь, что вы не хотите умножать поплавки или отрицательные числа, то;)

Ответ 18

Как говорили другие, ++ я может быть более эффективным, чем я ++, если я является экземпляром какого-либо объекта. Эта разница может быть или не быть существенной для вас.

Однако в контексте вашего вопроса о том, может ли компилятор сделать эти оптимизации для вас, в выбранном вами примере он не может. Причина в том, что ++ я и я ++ имеют разные значения - вот почему они реализованы как разные функции. я ++ должен выполнять дополнительную работу (чтобы скопировать текущее состояние до приращения, выполнить инкремент, а затем вернуть это состояние). Если вам не нужна эта дополнительная работа, тогда зачем выбирать ту форму другой более прямой формы? Ответ может быть читабельностью - но в этом случае стало ясно, что в С++ написано ++ i, поэтому я не считаю, что читаемость приходит в него.

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

Ответ 19

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

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

Ответ 20

  • Я вообще не оптимизирую ниже, чем сложность O (f (n)), если я не пишу на встроенном устройстве.

  • Для типичной работы g++/Visual Studio я предполагаю, что основные оптимизации будут надежно выполнены (по крайней мере, когда запрашивается оптимизация). Для менее зрелых компиляторов это предположение, вероятно, недействительно.

  • Если бы я делал тяжелую работу над потоками данных, я бы проверял способность компилятора испускать SIMD-инструкции.

  • Я бы предпочел настроить мой код на разные алгоритмы, чем конкретная версия конкретного компилятора. Алгоритмы выдержат испытание нескольких процессоров/компиляторов, тогда как если вы настроитесь на версию Visual С++ (первая версия) 2008 года, ваши оптимизации могут даже не работать в следующем году.

  • Некоторые оптимизационные трюки, которые очень разумны на старых компьютерах, сегодня имеют проблемы. Например, операторы ++/++ были разработаны вокруг более старой архитектуры, у которой была инструкция по приращению, которая была очень быстрой. Сегодня, если вы делаете что-то вроде

    for(int i = 0; i < top; i+=1)

    Я бы предположил, что компилятор оптимизирует i+=1 в инструкции inc (если бы у ЦП имелось это).

  • Классический совет - оптимизировать сверху вниз.

Ответ 21

Прежде всего - всегда выполняйте профилирование для проверки.

Во-первых, если вы оптимизируете правую часть кода. Если код запускается 1% от общего времени, забудьте. Даже если вы увеличили его на 50%, вы получите всего 0,5% общего ускорения. Если вы не делаете что-то странное, ускорение будет намного медленнее (особенно если вы использовали хороший оптимизационный компилятор). Во-вторых, если вы правильно его оптимизируете. Какой код будет работать быстрее на x86?

inc eax

или

add eax, 1

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

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

В целом - делайте это только в том случае, если вы должны и/или знаете, что делаете. Для этого потребуется объем знаний о коде, компиляторе и низкоуровневой компьютерной архитектуре, которые не учитываются в вопросе (и, честно говоря, я не знаю). И, скорее всего, ничего не получится. Если вы хотите оптимизировать - сделайте это на более высоком уровне.

Ответ 22

Я все еще делаю такие вещи, как ra < <= 1; вместо ra * = 2; И будет продолжаться. Но компиляторы (как бы плохи они ни были) и, что более важно, скорость компьютеров настолько быстро, что эти оптимизации часто теряются в шуме. Как общий вопрос, нет, это не стоит того, если вы специально используете ограниченную ресурсами платформу (скажем, микроконтроллер), где каждый дополнительный такт действительно учитывается, то вы, вероятно, уже это сделали и, вероятно, делаете довольно много ассемблерной настройки. Как привычка, я стараюсь не давать компилятору слишком много дополнительной работы, но для удобочитаемости кода и надежности я не ухожу с дороги.

Нижняя строка для производительности, хотя и не изменилась. Найдите способ кода, чтобы найти плотные висячие фрукты и исправить их. Майк Д. ударил ноготь по голове в ответ. Я слишком много раз видел, как люди беспокоятся о конкретных строках кода, не понимая, что они либо используют плохой компилятор, либо изменяя один параметр компилятора, они могут видеть в несколько раз увеличение производительности исполнения.