Почему условный ход не уязвим для отказа от ветвления?

Прочитав этот пост (ответ на Qaru) (в разделе по оптимизации), мне стало интересно, почему условные перемещения не уязвимы для ошибки прогнозирования ветвлений. Я нашел на статью о перемещении cond здесь (PDF от AMD). Кроме того, они заявляют о преимуществах производительности cond. движется. Но почему это? Я этого не вижу В тот момент, когда эта ASM-инструкция оценивается, результат предыдущей CMP-инструкции еще не известен.

Ответ 1

Mis-предсказанные ветки дороги

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

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

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

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

Условный ход никогда не бывает очень дорогим

Условный ход не требует предсказания, поэтому он никогда не может получить это наказание. Он имеет зависимости данных, как и обычные инструкции. Фактически, условное перемещение имеет больше зависимостей данных, чем обычные инструкции, поскольку зависимости данных включают в себя как "условие истинного", так и "ложное" условие. После команды, которая условно перемещает r1 в r2, содержимое r2 похоже, зависит как от предыдущего значения r2, так и от r1. Хорошо спрогнозированная условная ветвь позволяет процессору выводить более точные зависимости. Но для зависимостей данных обычно требуется один-два цикла, если им нужно время, чтобы прийти ко всем.

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

Ответ 2

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

CMOV

    add     eax, ebx
    cmp     eax, 0x10
    cmovne  ebx, ecx
    add     eax, ecx

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

Возможно, но CPU все еще знает, что инструкция, следующая за cmov, будет выполнена сразу после, независимо от результата команды cmp и cmov. Таким образом, следующая команда может быть безопасно доставлена ​​/декодирована раньше времени, что не относится к ветвям.

Следующая команда может выполнить даже до cmov (в моем примере это будет безопасно)

филиал

    add     eax, ebx
    cmp     eax, 0x10
    je      .skip
    mov     ebx, ecx
.skip:
    add     eax, ecx

В этом случае, когда декодер CPU видит je .skip, ему нужно будет выбрать, следует ли продолжать инструкции предварительной выборки/декодирования либо 1) из следующей команды, либо 2) из ​​цели перехода. ЦП догадается, что эта форвардная условная ветвь не произойдет, поэтому следующая инструкция mov ebx, ecx войдет в конвейер.

Через пару циклов выполняется je .skip и берется ветка. О, дерьмо! В нашем конвейере теперь есть случайный мусор, который никогда не должен выполняться. ЦПУ должен очистить все свои кэшированные инструкции и начать работу с .skip:.

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

Ответ 3

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

Рассмотрим следующий пример:

cmoveq edx, eax
add ecx, ebx
mov eax, [ecx]

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

Вариант разветвления может быть:

    jne skip
    mov edx, eax
skip:
    add ecx, ebx
    mov eax, [ecx]

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

Ответ 4

Вы должны прочитать их. С Fog + Intel просто найдите CMOV.

критика Линуса Торвальда CMOV около 2007 г.
Сравнение микроархитектур Agner Fog
Справочное руководство по оптимизации архитектуры Intel® 64 и IA-32

Короткий ответ, правильные предсказания являются "свободными", а условные ветки ошибочных прогнозов могут стоить 14-20 циклов на Хасуэлле. Однако CMOV никогда не бывает свободным. Тем не менее, я думаю, что CMOV сейчас намного лучше, чем когда Торвальдс сдался. На всех процессорах, когда-либо отвечавших, нет ни одного правильного на все времена.

Ответ 5

У меня есть иллюстрация со слайда [Peter Puschner et al.], В которой объясняется, как она преобразуется в код с одним путем, и ускоряется выполнение.

enter image description here