Как стать автором
Обновить

Комментарии 11

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

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

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

Это оптимизация по размеру, а как это влияет на скорость?

Потенциально увеличивается (меньше читать команд из памяти). А реально - да какая разница в каком регистре адрес, в RSI или в RBX?

Ну предположим код стал меньше, но как это сказалось на производительности?

Даже время компиляции, несмотря на 16 вариантов, замедлилась не критично: было 3 секунды, стало 48 секунд.

Ничего себе "не критично"

Еще одна такая мета-оптимизация и можно идти заваривать чай. Особенно здорово будет словить ошибку компиляции

Если была минута, а стало - 16, то да. А так, что Вы сделаете за 45 секунд? А ошибки как выдавались на экран, так и выдаются. По умолчанию после ошибки все останавливается и ждет нажатия клавиш. В том числе клавиши прекращения. Кроме этого, ошибка наверняка проявится на первом же варианте и остальные 15 уже можно не выполнять.

Только в реальности процессор регистры переименовывает. Все эти rax-r15 в некотором роде виртуальные. В настоящем процессоре будет больше сотни регистров общего назначения. Что касается плотности кода, то больше 32 байт и 5 инструкций за такт всё равно не декодируется, поэтому слишком уж уплотнять тоже нет смысла.

в реальности процессор регистры переименовывает

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

Что касается плотности кода

Может иметь значение на тяжёлых enterprise приложениях, когда icache/itlb миссы отъедают значительную часть времени. Но тогда надо на чём то таком и тестировать (и вопрос, каким будет время компиляции с метаоптимизацией для бинарника в сотни мегабайт).

5 инструкций за такт

Golden Cove вроде до шести расширили.

Может иметь значение на тяжёлых enterprise приложениях, когда icache/itlb миссы отъедают значительную часть времени.

Возможно, ошибаюсь, но мне всегда казалось, что icache или itlb miss случится только если переход не предсказался, и при этом branch target далеко относительно инструкции перехода. Всё таки instruction fetch работает спекулятивно. Скорее это будет, на indirect call/jump, и с этим надо бороться девиртуализацией методов.

push/pop - две load/store операции с зависимостью

Если мне память не изменяет, то в том же golden cove сделали memory renaming как раз для такого. Но это только пытается порвать зависимость по данным, store и load всё равно выполняются, травят кэш и греют атмосферу.

Всё таки instruction fetch работает спекулятивно

Оно, конечно, запустит чтение из памяти (возможно, с пробегом по таблице страниц) заранее, но текущие инструкции пролетят явно быстрее, чем новые дойдут до декодера.

в том же golden cove сделали memory renaming

Немного погуглил - вроде оно появилось на "атомном" Gracemont, но может чего то не вижу.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации