avatar
Миша пишет код
@misha_writes_code
01.02.2026 15:36
Смотрел тут свежее выступление Годболта. Он рассказывает про всякие фичи Compiler Explorer - много всего интересного, но удивил меня следующий прикол.

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

НО!

Загадка:

Если дополнительно указать компилятору, что код нужно собирать именно под skylake, то неожиданно, помимо popcnt dst, src, там появляется еще и xor dst, dst, который выглядит лишним, ведь popcnt не использует dst в вычислениях, а просто кладет туда результат. Зачем же он тогда нужен?

https://godbolt.org/z/hYG7zEs4Y

Разгадка:

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

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

popcnt dst, src зависела не только от src, но также и от dst. Из-за чего инструкция могла выполняться заметно дольше, потому что ждала фантомную зависимость от dst.

Чтобы эту зависимость уничтожить, уже компилятор вынужден на своей стороне дополнительно что-то предпринимать. По-видимому, самое простое и дешевое - вставить зануление регистра через xor dst, dst, который гарантированно изменит его значение и сломает цепочку зависимостей.

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

P.S.
Меня еще удивило, что на вопрос со сцены "а почему там появился лишний xor", в зале нашелся человек, который корректно на это ответил. Видимо, это какая-то популярная тема и только я как всегда был не в курсе.

Всякие ссылки по теме:

- фикс в clang: https://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20160215/332717.html
- фикс в gcc: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62011
- исходный вопрос на stackoverflow: https://stackoverflow.com/questions/25078285/replacing-a-32-bit-loop-counter-with-64-bit-introduces-crazy-performance-deviati
- и еще ответ со списком похожих проблем:
https://stackoverflow.com/a/70649222
🔥 11
👍 3
😁 3
8 904

Обсуждение 0

Обсуждение не доступно в веб-версии. Чтобы написать комментарий, перейдите в приложение Telegram.

Обсудить в Telegram