Как возможно переупорядочение load-> store с фиксацией по порядку? - PullRequest
0 голосов
/ 07 сентября 2018

ARM позволяет переупорядочивать загрузки с последующими хранилищами, так что следующий псевдокод:

// CPU 0 | // CPU 1 temp0 = x; | temp1 = y; y = 1; | x = 1;

может привести к temp0 == temp1 == 1 (и это также наблюдается на практике). У меня проблемы с пониманием, как это происходит; Кажется, что коммит по порядку предотвратил бы это (что, насколько я понял, присутствует практически во всех процессорах OOO). Мое рассуждение гласит: «нагрузка должна иметь свое значение до ее фиксации, она фиксируется до хранилища, и значение хранилища не может стать видимым для других процессоров, пока она не зафиксирует».

Я предполагаю, что одно из моих предположений должно быть неверным, и должно выполняться что-то вроде одного из следующих:

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

  • Загрузка может зафиксироваться до того, как станет известно ее значение. Я не догадываюсь, как это будет реализовано.

  • Магазины могут стать видимыми, прежде чем они будут совершены. Может быть, буферу памяти где-то разрешено пересылать хранилища для загрузки в другой поток, даже если загрузка была поставлена ​​в очередь раньше?

  • Что-то еще целиком?

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

1 Ответ

0 голосов
/ 07 сентября 2018

Все ваши предположения выглядят корректно, за исключением того, что вы можете построить uarch, где нагрузки могут отключаться от ядра OoO, просто проверив разрешения (TLB) для нагрузки, чтобы убедиться, что она определенно может произойти. Это могут быть OoO exec CPU, которые это делают (обновление: очевидно, есть).

Я думаю, что процессорам x86 требуются нагрузки для фактического получения данных до того, как они смогут удалиться, но их сильная модель памяти в любом случае не позволяет переупорядочивать LoadStore. Так что ARM определенно может быть другим.

Вы правы, что магазины не могут быть видны другим ядрам до выхода на пенсию. Так лежит безумие. Даже на SMT ядре (несколько логических потоков на одном физическом ядре) оно связывает спекуляции на двух логических потоках вместе, требуя, чтобы они оба откатывались, если один из них обнаружил неправильное предположение. Это лишило бы SMT смысла использовать один логический поток в других.

(Связанный: создание удаленных, но еще не зафиксированных (для L1d) хранилищ, видимых для других логических потоков на том же ядре, - это то, как некоторые реальные реализации PowerPC позволяют потокам расходиться во мнениях относительно глобального порядка хранилищ. Будет две атомарные записи в разные места в разных потоках всегда будут рассматриваться в одном и том же порядке другими потоками? )


Процессоры с выполнением по порядку могут начинать загрузку (проверить TLB и записать запись буфера загрузки) и останавливаться только в том случае, если инструкция пытается использовать результат до того, как она будет готова. Тогда более поздние инструкции, включая магазины, могут работать нормально . Это в основном требуется для не страшной производительности в конвейере в порядке; остановка при каждой пропадании кэша (или даже просто задержка L1d) была бы неприемлемой. Параллелизм памяти - вещь даже для обычных процессоров; у них может быть несколько буферов загрузки, которые отслеживают несколько незавершенных ошибок кэша. Высокопроизводительные процессорные ядра ARM, такие как Cortex-A53 , по-прежнему широко используются в современных смартфонах.

Таким образом, если загрузка отсутствует в кеше, но хранилище срабатывает (и фиксирует L1d до того, как более ранние загрузки с пропуском кэша получат свои данные), вы можете выполнить переупорядочение LoadStore. ( Jeff Preshing ввод в запись памяти использует этот пример для LoadStore, но совсем не вдавается в детали uarch.)

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

Итак, последовательность в конвейере по порядку:

  • lw r0, [r1] Удар TLB, но отсутствует в кэше L1d. Модуль исполнения загрузки записывает адрес (r1) в буфер загрузки. Любая более поздняя инструкция, которая пытается прочитать r0, остановится, но мы точно знаем, что загрузка не сработала.

    Если r0 привязано к ожиданию готовности этого загрузочного буфера, сама команда lw может покинуть конвейер (удалить), как и более поздние инструкции.

  • любое количество других инструкций, которые не читаются как r0. Это остановит конвейер в порядке.
  • sw r2, [r3] исполнительный модуль хранилища записывает адрес + данные в буфер / очередь хранилища. Тогда эта инструкция может уйти в отставку.

    При проверке загрузочных буферов обнаруживается, что это хранилище не перекрывается с ожидающей загрузкой, поэтому оно может зафиксировать в L1d. (Если в перекрыто , вы не сможете зафиксировать в любом случае, пока MESI RFO не завершится, и быстрый перезапуск перенаправит входящие данные в загрузочный буфер. Так что, возможно, не слишком сложно обработать этот случай, даже не исследуя каждое хранилище, но давайте рассмотрим только строку отдельного кэша случай, когда мы можем получить переупорядочение LoadStore)

    Обязательство L1d = становится глобально видимым. Это может произойти, когда предыдущая загрузка все еще ожидает поступления строки кэша.


Для ЦПУ OoO вам понадобится какой-нибудь способ привязать завершение загрузки обратно к ядру OoO для инструкций, ожидающих результата загрузки. Я предполагаю, что это возможно, но это означает, что значение архитектуры / выхода на пенсию регистра может не храниться где-либо в ядре. Сбросы конвейера и другие откаты от неправильных предположений должны были бы зависеть от этой связи между входящей нагрузкой и физическим и архитектурным регистром. (Не очищать буферы хранилища при откате конвейера - это уже то, что ЦПУ должны делать, хотя. Уходящие в отставку, но еще не зафиксированные хранилища, находящиеся в буфере хранилища, не имеют возможности откатиться.)

Это может быть хорошей идеей для уархов с маленьким окном OoO, которое слишком мало, чтобы приблизиться к сокрытию промаха кэша.


У нас есть экспериментальные доказательства переупорядочения LoadStore на OoO ARM: в разделе 7.1 https://www.cl.cam.ac.uk/~pes20/ppc-supplemental/test7.pdf показаны ненулевые значения для «буферизации нагрузки» на Tegra 2 , которая основана на Cortex-A9 уарч . Я не посмотрел все остальные, но я переписал ответ, чтобы предположить, что это вероятный механизм и для вышедших из строя процессоров. Я точно не знаю, так ли это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...