Как я понимаю барьеры чтения памяти и летучие



некоторые языки обеспечивают volatile модификатор, который описывается как выполнение "барьера чтения памяти" перед чтением памяти, которая поддерживает переменную.



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



Итак, volatile действительно гарантирует, что актуальное значение считывается или просто (задыхается!) что значения, которые считываются, по крайней мере, так же актуальны, как и чтения перед барьером? Или какая-то другая интерпретация? Каковы практические последствия из этого ответа?

957   2  

2 ответов:

есть читать препятствия и барьеры записи; приобрести барьеров и выхода барьеры. И многое другое (io vs memory и т. д.).

барьеры не существуют, чтобы контролировать "последнее" значение или "свежесть" значений. Они предназначены для управления относительным порядком доступа к памяти.

барьеры записи контролируют порядок записи. Поскольку запись в память выполняется медленно (по сравнению со скоростью процессора), обычно существует очередь запросов на запись, в которой записи размещаются раньше они действительно произойдут. Хотя они поставлены в очередь по порядку, в то время как внутри очереди записи могут быть переупорядочены. (Так что, возможно, "очередь" - не самое лучшее название...) Если вы не используете барьеры записи для предотвращения переупорядочения.

Read барьеры контролируют порядок чтения. Из-за спекулятивного выполнения (CPU смотрит вперед и загружает из памяти рано) и из-за существования буфера записи (CPU будет читать значение из буфера записи вместо памяти, если он есть-т. е. CPU думает, что он просто написал X = 5, тогда зачем читать его обратно, просто смотрите, что он все еще ждет стать 5 в буфере записи) чтение может происходить не по порядку.

Это верно независимо от того, что компилятор пытается сделать в отношении порядка сгенерированный код. ie 'volatile' в C++ здесь не поможет, потому что он только говорит компилятору выводить код для повторного чтения значения из "памяти", он не говорит процессору, как / где его читать (т. е. "память" - это много вещей уровень процессора).

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

какие блоки? - приобретение и / или освобождение блоков.

приобрести - например для чтения приобретать(х) будет добавлена чтение х в чтения очередь и очистить очередь (не совсем очистить очередь, но добавить маркер говоря не переупорядочивать ничего перед этим читать, это как если бы очередь была сброшена). Таким образом, более поздние (в порядке кода) чтения могут быть переупорядочены, но не до чтения x.

релиз - например пишите-релиз(х, 5) сбрасывать (или маркер) в очереди первый, то добавить пишите запрос по записи-очереди. Таким образом, более ранние записи не будут переупорядочены после x = 5, но обратите внимание, что более поздние записи могут быть переупорядочены до x = 5.

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

приобретение и освобождение считаются "полу-барьерами" или "полу-заборами", потому что они только останавливают переупорядочивание в одном направлении.

полный барьер (или полный забор) применяется как к приобретению, так и к выпуску - т. е. без переупорядочения.

обычно для lockfree программирования, или C# или java 'volatile', то, что вы хотите / нужно чтение-приобретение и запись-релиз.

ie

void threadA()
{
   foo->x = 10;
   foo->y = 11;
   foo->z = 12;
   write_release(foo->ready, true);
   bar = 13;
}
void threadB()
{
   w = some_global;
   ready = read_acquire(foo->ready);
   if (ready)
   {
      q = w * foo->x * foo->y * foo->z;
   }
   else
       calculate_pi();
}

Итак, прежде всего, это плохой способ программные потоки. Замки были бы безопаснее. Но только для иллюстрации барьеров...

после того, как threadA () закончит писать foo, ему нужно написать foo - > ready LAST, really last, иначе другие потоки могут увидеть foo - >ready early и получить неправильные значения x/y / z. поэтому мы используем write_release на foo - >ready, который, как упоминалось выше,эффективно "очищает" очередь записи (гарантируя,что x, y, z зафиксированы), затем добавляет ready=true запрос к очереди. А затем добавляет бар=13 запрос. Обратите внимание, что так как мы просто используемый барьер освобождения (не полный) бар=13 может быть записан до готовности. Но нам все равно! т. е. мы предполагаем, что бар не изменяет общие данные.

теперь threadB () должен знать, что когда мы говорим "готов", мы действительно имеем в виду готов. Так что мы делаем read_acquire(foo->ready). Это чтение добавляется в очередь чтения,затем очередь сбрасывается. Обратите внимание, что w = some_global также может быть в очереди. Так что foo - > ready можно прочитать доsome_global. Но опять же, нам все равно, так как это не является частью важные данные, о которых мы так заботимся. То, что нас волнует,-это foo->x/y/z. поэтому они добавляются в очередь чтения после получения flush/marker, гарантируя, что они будут прочитаны только после чтения foo - >ready.

Обратите также внимание, что это, как правило, те же самые барьеры, используемые для блокировки и разблокировки мьютекса/CriticalSection/etc. (т. е. получить на замок (), отпустите на разблокировку ()).

и

  • Я уверен, что это (т. е. acquire/release)-это именно то, что говорят MS docs для чтения/записи "изменчивых" переменных в C# (и, возможно, для MS C++, но это нестандартно). Смотрите http://msdn.microsoft.com/en-us/library/aa645755 (VS.71).aspx в том числе "изменчивое чтение имеет "семантику"; то есть оно гарантированно происходит до любых ссылок на память, которые происходят после него..."

  • Я думаю java то же самое, хотя я не так знаком. Я подозреваю, что это точно так же, потому что вам просто обычно не нужно больше гарантий, чем read-acquire/write-release.

  • в вашем вопросе Вы были на правильном пути, думая, что это действительно все об относительном порядке - у вас просто были порядки назад (т. е. "значения, которые читаются, по крайней мере, такие же современные, как и чтение перед барьером? "- нет, читает перед барьером неважны, его читает после барьера, которые гарантированно будут после, наоборот для пишет).

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

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

volatile в большинстве языков программирования не подразумевает реального барьера памяти чтения ЦП, но приказ компилятору не оптимизировать чтение через кэширование в регистре. Это означает, что процесс чтения/поток получит значение "в конечном итоге". Распространенным методом является объявление логического volatile флаг устанавливается в обработчике сигнала и проверяется в основном цикле программы.

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

Чтобы ответить на ваш вопрос-нет, барьер памяти не гарантирует "последнее" значение, но гарантирует ордер операций доступа к памяти. Это имеет решающее значение, например, в блокировка-Free программирование.

здесь является одним из праймеров на барьерах памяти процессора.

Comments

    Ничего не найдено.