Почему нужно использовать общий барьер для гарантии транзитивности процессора?
Недавно я прочитал транзитивность процессора в барьерах памяти, и автор подчеркивает, что только общий барьер может гарантировать транзитивность. Но я не очень хорошо понимаю это. Например:
CPU 1 CPU 2 CPU 3
======================= ======================= =======================
{ X = 0, Y = 0 }
STORE X=1 LOAD X STORE Y=1
<read barrier> <general barrier>
LOAD Y LOAD X
Предположим, что X в кеше CPU3, и статус изменен, Y в кеше CPU2, и статус также изменен.
CPU1 разделяет свой буфер хранения с CPU2, если мы добавим барьер записи перед барьером чтения. (это становится общим барьером)
1) CPU1 устанавливает значение X(X=1) в буфере хранения.
2) CPU2 считывает значение X из буфера хранилища (буфер общего хранилища).
3) CPU2 помечает X в буфере хранения (барьер записи) и читает очередь недействительных, чтобы гарантировать, что сообщения от CPU3(барьер чтения) не будут аннулированы.
4) CPU2 хочет изменить строку кэша X с недействительной на измененную, поэтому отправляет сообщения о недействительности в CPU3.
5) CPU3 получает недействительные сообщения X, помещает их в очередь аннулирования и отвечает на CPU2.
6) CPU2 получает ответ, затем записывает X = 1 в память или кэш и загружает Y == 0.
...
7) CPU3 обнаружит, что имеет недействительное сообщение X в своей недействительной очереди, когда он выполняет общий барьер, после этого X должно быть равно 1.
Все в порядке, я могу понять. Однако я прочитал другой пример из рисунка 14.3 perbook, как показано ниже:
thread0(void) {
A = 1;
smp_wb();
B = 1;
}
thread1(void) {
while (B == 0)
continue;
barrier();
C = 1;
}
thread2(void) {
while (C == 0)
continue;
barrier();
assert(A == 1);
}
Есть несколько возможностей отстаивать огонь. Автор сказал, что изменить все барьеры на smp_mb можно исправить в ответе на Quick Quiz 14.2.
Итак, мой вопрос: зачем нам менять барьер в thread1 на smp_mb? Если thread0 и thread1 работают на CPU0 и CPU1, и они совместно используют буфер хранилища. Их буфер хранения будет как bleow после того, как thread1 выполнит Store C = 1.
[A (wb), B, C]
Поскольку thread2(работает на CPU2) также использует smp_mb вместо барьера, поэтому он гарантирует, что A должно быть 1, если он видит C == 1.
Я описываю все вышеперечисленное в протоколе согласованности памяти MESI. Может быть, автор подразумевает, что существуют другие протоколы, которые должны создавать барьер в thread1 вместо smp_mb, чтобы гарантировать транзитивность процессора?
Кто-нибудь может дать мне пример, пожалуйста?
Может быть, это ошибка думать о транзитивности в конкретном протоколе. Мы должны помнить, что rmb() или wmb() не могут гарантировать транзитивность процессора, потому что существует очень много разных протоколов и архитектур.