C ++ Как выпустить и получить можно на x86 только с помощью MOV? - PullRequest
7 голосов
/ 20 февраля 2020

Этот вопрос является продолжением / разъяснением к этому:

Реализует ли инструкция MOV x86 хранилище C ++ 11 memory_order_release atomi c?

Это говорит о том, что инструкции по сборке MOV достаточно для выполнения семантики получения-выпуска на x86. Нам не нужны LOCK, заборы или xchg et c. Однако я изо всех сил пытаюсь понять, как это работает.

Intel do c Vol 3A Глава 8 гласит:

https://software.intel.com/sites/default/files/managed/7c/f1/253668-sdm-vol-3a.pdf

В однопроцессорной (основной) системе ....

  • Чтения не переупорядочиваются при других чтениях.
  • Писания не переупорядочиваются при более старых чтениях.
  • Записи в память не переупорядочиваются с другими записями, за исключением следующих случаев:

, но это для одного ядра. В разделе о многоядерных процессорах, по-видимому, не упоминается, как применяются нагрузки:

В многопроцессорной системе применяются следующие принципы упорядочения:

  • Отдельные процессоры используют те же принципы упорядочения, что и в однопроцессорной системе.
  • Запись одним процессором выполняется в одном и том же порядке всеми процессорами.
  • Запись с отдельного процессора НЕ упорядочена по отношению к пишет с других процессоров.
  • Упорядочение памяти подчиняется причинно-следственной связи (упорядочение памяти учитывает транзитивную видимость).
  • Любые два хранилища видятся в согласованном порядке процессорами, отличными от тех, которые выполняют хранилища
  • Заблокированные инструкции имеют общий порядок.

Так как же один MOV может облегчить получение-освобождение?

Ответы [ 2 ]

5 голосов
/ 20 февраля 2020

но это для одного ядра. В многоядерном разделе, похоже, не упоминается, как применяются нагрузки:

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

Модель для переупорядочения доступа к разделяемой памяти является одноядерной моделью, то есть программный порядок + буфер хранения = в основном acq_rel. На самом деле немного сильнее, чем acq_rel, и это нормально.

Единственное переупорядочение, которое происходит, это local , внутри каждого ядра CPU . Как только магазин становится глобально видимым, он становится видимым для всех других ядер одновременно, и до этого не становился видимым ни для каких ядер. (За исключением того, что ядро ​​хранит хранилище через пересылку хранилища.) Вот почему для восстановления последовательной согласованности поверх модели S C + store-buffer достаточно только локальных барьеров. (Для x86 просто mo_seq_cst просто нужно mfence после того, как S C сохранит, чтобы истощить буфер хранилища, прежде чем могут быть выполнены любые дальнейшие загрузки. mfence и lock ed (которые также являются полными барьерами) don ' надо беспокоить другие ядра, просто заставьте это подождать).

Один ключевой момент, который нужно понять, это то, что является a согласованным общим представлением памяти (через связное кэши), которые разделяют все процессоры. В самом верху главы 8 Intel SDM определены некоторые из этих предпосылок:

Эти многопроцессорные механизмы имеют следующие характеристики:

  • Для обслуживания системы когерентность памяти - когда два или более процессора пытаются одновременно получить доступ к одному и тому же адресу в системной памяти, должен быть доступен некоторый механизм связи или протокол доступа к памяти, чтобы обеспечить согласованность данных и, в некоторых случаях, позволить одному процессору временно заблокировать область памяти .
  • Для поддержания целостности кэша - когда один процессор обращается к данным, кэшированным на другом процессоре, он не должен получать неверные данные. Если он изменяет данные, все остальные процессоры, которые обращаются к этим данным, должны получать измененные данные.
  • Чтобы обеспечить предсказуемое упорядочение операций записи в память - в некоторых случаях важно, чтобы записи в память наблюдались внешне точно так же порядок как запрограммирован.
  • [...]

Механизм кэширования и согласованность кэшей процессоров Intel 64 и IA-32 обсуждаются в главе 11.

(процессоры используют какой-либо вариант MESI ; Intel на практике использует MESIF, AMD на практике использует MOESI.)

В этой же главе также приведены некоторые лакмусовые тесты, которые помогают проиллюстрировать / определить модель памяти. Части, которые вы цитировали, на самом деле не являются строго формальным определением модели памяти. Но в разделе 8.2.3.2 «Ни нагрузки, ни хранилища не переупорядочиваются с помощью операций, подобных * *», показано, что нагрузки не переупорядочиваются с нагрузками. В другом разделе также показано, что Переупорядочивание LoadStore запрещено. Acq_rel в основном блокирует все переупорядочения, кроме StoreLoad, и именно это делает x86. (https://preshing.com/20120913/acquire-and-release-semantics/ и https://preshing.com/20120930/weak-vs-strong-memory-models/)

Похожие:


Другие ISA

В целом, наиболее слабые модели HW памяти также допускают только локальное переупорядочение, поэтому барьеры остаются только локальными в ядре ЦП, просто заставляя (некоторую часть) это ядро ​​ждать некоторого условия. (Например, x86 mfence блокирует последующую загрузку и сохранение от выполнения до тех пор, пока не будет исчерпан буфер хранилища. Другие ISA также извлекают выгоду из облегченных барьеров для эффективности для вещей, которые x86 применяет между каждой операцией памяти, например, блокирует переупорядочение LoadLoad и LoadStore. https://preshing.com/20120930/weak-vs-strong-memory-models/)

Несколько ISA (только PowerP C в наши дни) позволяют магазинам становиться видимыми для некоторых других ядер, прежде чем становятся видимыми для всех, , позволяя IRIW переупорядочивать . Обратите внимание, что mo_acq_rel в C ++ позволяет переупорядочивать IRIW; только seq_cst запрещает это. Большинство моделей памяти HW немного сильнее, чем ISO C ++, и делают это невозможным, поэтому все ядра согласуются с глобальным порядком магазинов.

3 голосов
/ 20 февраля 2020

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

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

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

Это дает нам четыре гарантии:

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

Просмотр гарантий:

  • Чтения не переупорядочиваются при других чтениях.
  • Записи не переупорядочиваются с более старыми операциями чтения.
  • Записи в память не переупорядочиваются с другими операциями записи [..]
  • Использование отдельных процессоров те же принципы упорядочения, что и в однопроцессорной системе.

Этого достаточно для обеспечения гарантий упорядочения.

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

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

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


  • Записывания одним процессором наблюдаются в одном и том же порядке всеми процессорами.

Этого достаточно, чтобы удовлетворить синхронизацию с выпуском-выпуском.

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

...