Pull to refresh

Comments 16

Правильнее сказать, что на большинстве современных платформ команда сложения (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, поэтому цикл получается очень компактным.

  1. Актуальный компилятор от Intel называется icx

  2. Есть такой чудный ресурс https://uica.uops.info/ который покажет вам реальные такты, вместо гадания, что быстрее.

Благодарю за ссылку на анализатор кода x86_64. Обязательно ознакомлюсь и протестирую.

Держите и по эльбрусу (спасибо @a1batross); что на VLIW ещё интересно в обсуждаемом плане, так это то, что можно посмотреть непосредственно исполняемое на железе, а не "внешние" последовательные команды, которые затем декодер пережуёт в совсем иное.

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

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

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

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

по асемблеру я не смотрел, но по синтаксису около этого вроде, я со стеком делал, поэтому запомнил

Автор, а где результаты измерений на реальном железе ?

В целом, "не верь написанному" в современнром мире как никогда верный подход.

Почему эта тема очень напоминает мне метафору про академию бубна в музыке?

Тут у нас академия инкремента в программировании.

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

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

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

Ещё сэр Сеймур Крей говаривал, что трюк не в том, чтоб построить быстрый процессор, а в том, чтобы построить быструю систему. Но есть нюанс...

Sign up to leave a comment.

Articles