Заборы памяти - нужна помощь, чтобы понять - PullRequest
11 голосов
/ 22 октября 2010

Я читаю Барьеры памяти Пола МакКенни http://www.rdrop.com/users/paulmck/scalability/paper/whymb.2010.07.23a.pdf все объясняется очень подробно, и когда я вижу, что все ясно, я сталкиваюсь с одним предложением, которое сводит на нет все и заставляет меня думать, что я ничего не понял. Позвольте мне показать пример

void foo(void)
{
   a = 1; #1
   b = 1; #2
}

void bar(void)
{
   while (b == 0) continue; #3
   assert(a == 1); #4
}

Допустим, эти две функции работают на разных процессорах. Теперь то, что могло бы произойти, это то, что сохранение в # 1 можно увидеть после сохранения в b # 2 вторым процессором, потому что очереди первого процессора сохраняются в «a» и переходят к сохранению инструкции b. Хорошо, это нормально, мы добавляем забор записи в строке между # 1 и # 2, но этот код все еще может завершиться ошибкой, потому что второй процессор может поставить в очередь сообщение о недействительности, поэтому мы добавляем еще один забор памяти (на этот раз читаем забор) в линия между № 4 и № 4.

void foo(void)
{
   a = 1; #1
   write_memory_barrier();
   b = 1; #2
}

void bar(void)
{
   while (b == 0) continue; #3
   read_memory_barrier();
   assert(a == 1); #4
}

это заставляет второй процессор обрабатывать все поставленные в очередь сообщения (сделать недействительными a) и снова прочитать его, отправив сообщение чтения MESI первому процессору на # 4. ХОРОШО. Далее в статье написано

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

Отлично, это понятно, но после этого я вижу это

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

так

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

, который смешивает все, что было объяснено ранее. Что это значит? Какую нагрузку в функции «bar» нужно завершить до загрузки «a» # 4? Я понимаю, что утверждение может завершиться с ошибкой без барьера памяти в этой функции только потому, что процессор может прочитать старое значение, потому что ему все еще не удалось аннулировать строку кэша, где находится объект "a".

Объяснение в деталях было бы очень полезно, я пытаюсь понять это весь день.

Большое спасибо заранее.

1 Ответ

10 голосов
/ 22 октября 2010

Что это значит?

Это означает, что если у вас есть:

read
read
read
READ BARRIER
read
read
read

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

Какие нагрузки в bar() должны завершиться до начала загрузки a (# 4)?

Все чтения b (# 3) должны предшествовать любому чтению a (# 4). Это означает, что a не читается до тех пор, пока b не перестанет 0. Поскольку foo() использует барьер записи, чтобы гарантировать, что a уже был изменен на 1 (# 1) к тому времени, когда b изменено (# 2). Таким образом, эти два барьера работают вместе, чтобы гарантировать, что утверждение assert всегда будет успешным.

...