Комментарии 17
Правильнее сказать, что на большинстве современных платформ команда сложения (ADD) выполняется быстрее, чем инкремент (INC).
Да неужели 😂
А это кстати неожиданная информация. Надеюсь автор на 100% уверен в ней. А то как то даже не верится
У инструкций процессора есть два параметра - латентность и пропускная способность. Формально они одинаковы что для инкремента, что для сложения. Но операция сложения устанавливает доп флаги, чего не делает inc и в принципе (в теории) может влиять на параллелизацию последующих инструкций.
Ну то есть вот такой цикл
xor rax, rax
mov rcx, 1000
L0: add rax, 1
cmp rax, rcx
jl L0может оказаться быстрее чем
xor rax, rax
mov rcx, 1000
L0: inc rax
cmp rax, rcx
jl L0за счёт того, что в первом случае последующие cmp и jl склеятся вместе, а во втором случае - нет. Я только что проверил это на Хасвелле, но разницы вообще не вижу, а современный ноут на рапторе я не стал домой тащить, это я в понедельник проверю.
Советы из 1970х не работают потому что тогда не было в процессорах нескольких конвейеров, предсказания вычислений, кэшей памяти и т.д., тогда операции исполнялись последовательно и компилировались как написано.
Современные компиляторы далеко ушли, включенный оптимизатор сам заменит i++ на ++i если это действительно дает какое-то ускорение. В оптимизаторы зашиты наиболее эффективные решения компиляции, которые позволят по максимуму использовать скрытые резервы процессора.
Если есть возможность затестите компилятор от Intel и скорее всего получите еще более быстрое решение.
Вобщем я к тому что надо писать читаемый код, а о том как его быстрее выполнить пусть заботится компилятор, поверьте, он с этим справится лучше вас.
Доброго дня! Про компилятор полностью согласен. В руководствах по оптимизации приложений приводят следующие шаги оптимизации (в порядке приоритета):
1) Изучите и включите параметры оптимизации компилятора.
2) Если оптимизации компилятора не хватает, то поищите фреймворки или библиотеки, оптимизированные под вашу платформу, и замените свой код на них.
3) Запустите профайлер, найдите узкие места. Попытайтесь оптимизировать алгоритм и структуры данных. Например, использование «дерева» значительно сократит число итераций поиска (или в целом лишней работы) относительно связного списка.
4) Используйте оптимизацию на уровне ассемблера (ISA).
5) Оптимизируйте работу с кэшем.
6) Оптимизируйте код под особенности микроархитектуры (объединение операций, повышение пропускной способности инструкций и т.д.).
Спасибо за напоминание о компиляторе Intel. Обязательно его протестирую.
Пока проверил в Compiler Explorer поведение x86‑64 ICC 2021.6.0. Без оптимизации в обоих циклах используется сложение. При -O3 для первого цикла – INC, для второго – смесь ADD и INC. При -Os для первого цикла – INC, для второго – ADD, но все команды векторные, в отличие от MSVC, поэтому цикл получается очень компактным.
Актуальный компилятор от Intel называется icx
Есть такой чудный ресурс https://uica.uops.info/ который покажет вам реальные такты, вместо гадания, что быстрее.
Благодарю за ссылку на анализатор кода x86_64. Обязательно ознакомлюсь и протестирую.
Держите и по эльбрусу (спасибо @a1batross); что на VLIW ещё интересно в обсуждаемом плане, так это то, что можно посмотреть непосредственно исполняемое на железе, а не "внешние" последовательные команды, которые затем декодер пережуёт в совсем иное.
Кстати, в упомянутой Вами книжке так и разбираются примеры распутывания зависимостей по данным в циклах -- наглядно видно плотность команд в тактах.
Мейерс не говорит про примитивные типы, он про сложные: постинкремент размножает объекты, пред- - нет. В большинстве случаев это некритично, компиляторы сейчас умные, но привычка у меня осталась.
насколько я знаю, инкремент зависит от реализации, если инкремент просто по офсету стека-текущего кадра фрейма, берёт и делает +1 это 1 ситуация, когда инкремент пушится/грузится/инкрементируется/загружается это другое, это разные операции с точки зрения определения инкремента получается, но технически они делают 1 и тоже поидее
по асемблеру я не смотрел, но по синтаксису около этого вроде, я со стеком делал, поэтому запомнил
Автор, а где результаты измерений на реальном железе ?
В целом, "не верь написанному" в современнром мире как никогда верный подход.
Почему эта тема очень напоминает мне метафору про академию бубна в музыке?
Тут у нас академия инкремента в программировании.
В программировании мы постоянно балансируем между читаемостью кода, удобством сопровождения, эффективностью разработки и производительностью, и даже внутри каждой из этих областей. Например, оптимизация производительности часто предполагает компромисс между экономией памяти, скоростью выполнения и сложностью реализации. В реальных проектах большинство участков кода не требуют максимальной производительности. Приоритет удобства сопровождения и читаемости, как правило, перевешивает экономию каждого байта или микросекунды.
Некоторые оптимизации работают на любой платформе и для любых моделей процессоров, но другие могут работать только на определенных платформах и для определенных моделей процессоров. Поэтому, прежде чем внедрять какое-либо из них, пожалуйста, проведите тестирование.
Как хорошо, что современные системы упираются чаще не в процессор, а в диск, сеть или на худой конец в объекты многопоточной синхронизации, чтобы не приходилось себя грузить тем, какой инкремент эффективнее (сарказм, но так и есть).
C был создан на платформе PDP11, смотрим для примера PDP11/04 handbook, instruction timing. Двухоперандная команда сложения регистров 3.17 мкс. Однооперандная команда инкремента регистра 2.65 мкс. Команда сравнения регистра с непосредственным значением 3.17+2.66=5.83 мкс. Команда сравнения регистра с непосредственным значением и постинкрементом регистра 3.17+2.66+1.20=7.03. Выигрыш использования очевиден. Собственно синтаксис C делался под платформу PDP11, где непосредственно в ISA есть пост/пре инкремент/декремент регистров.

Может ли устареть инкремент: обзор выполнения оператора на современных вычислительных платформах