Упорядочение памяти в C ++ 11 связано с упорядочением сброса основной памяти?

Я не уверен, что полностью понимаю (и, возможно, ошибаюсь) концепции атомарности и упорядочения памяти в C ++ 11. Возьмем этот простой однопоточный пример:

int main()
{
    std::atomic<int> a(0);
    std::atomic<int> b(0);
    a.store(16);
    b.store(10);

    return 0;
}

В этом однопоточном коде, если бы a и b не были атомарными типами, компилятор мог бы переупорядочить инструкцию таким образом, что в ассемблерном коде у меня, например, есть инструкция перемещения для присвоения 10 значению 'b' перед инструкцией перемещения для присвоено 16 как «а». Так что для меня, будучи атомарными переменными, он гарантирует мне, что у меня будет «инструкция перемещения» перед «инструкцией перемещения b», как я сказал в моем исходном коде. После этого идет процессор с его исполнительным блоком, инструкциями предварительной выборки и блоком не по порядку. И этот процессор может обрабатывать «инструкцию b» перед «инструкцией», независимо от порядка инструкций в ассемблерном коде. Таким образом, я могу сохранить 10 в регистре или в буфере хранилища процессора или в кэш-памяти, прежде чем я сохраню 16 в буфере регистров / хранилищ или в кеше.

И, насколько я понимаю, именно здесь появляется модель упорядочивания памяти. С этого момента, если я позволю модели по умолчанию последовательно согласовываться. Один из них гарантирует, что очистка этих значений (10 и 16) в основной памяти будет соответствовать порядку сохранения в исходном коде. Так что процессор начнет очищать регистр или кеш, где 16 хранится в основной памяти для обновления «a», а после этого он сбросит 10 в основную память для «b».

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

Извините, если у вас возникнут проблемы с чтением меня, я все еще плохо говорю по-английски. Но спасибо, ребята, что уделили время.


person jedib    schedule 16.04.2015    source источник
comment
В вашем коде переменные, очевидно, не видны никаким другим потоком (или даже любой другой функцией), поэтому они могут быть скомпилированы точно так же, как неатомарные переменные.   -  person curiousguy    schedule 10.12.2019


Ответы (2)


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

В вашем примере модель памяти заявляет, что, поскольку запись в a происходит - до записи в b, любой поток, который читает 10 из b, должен при последующих чтениях из a видеть 16 (если это с тех пор не было перезаписано, конечно ).

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

  • Ваше понимание оборудования может быть неправильным. Аппаратное обеспечение даже сложнее, чем модель памяти C ++.
  • Даже если вы сейчас правильно поняли, более поздняя версия оборудования может иметь другую модель, по крайней мере, в подсистемах.
  • Сопоставляя аппаратную модель, вы можете сделать неправильные предположения о последствиях для другой аппаратной модели. Например. Если вы понимаете, как модель памяти отображается на оборудование x86, вы не поймете тонкой разницы между потреблением и получением на PowerPC.
  • Модель C ++ очень хорошо подходит для рассуждений о правильности.
person Sebastian Redl    schedule 16.04.2015
comment
Спасибо за ответы. Вы правы, не стоит думать с точки зрения оборудования. И что касается атомарности, правильно ли я думаю, что атомарность не позволяет компилятору переупорядочивать инструкции? Он как будто ставит какие-то заграждения (заборы)? - person jedib; 16.04.2015
comment
В некоторой степени это предотвращено. Некоторые переупорядочения допустимы, так же как и перемещение нагрузки до блокировки мьютекса в критическую секцию. - person Sebastian Redl; 16.04.2015
comment
Вы поднимаете хорошие вопросы, но всегда полезно знать, что делает ваше HW, даже ради оптимизации. Пока вы позволяете компилятору переводить модель упорядочения памяти в HW, вы должны быть в безопасности. - person Leeor; 18.05.2015
comment
Формулировку standardese очень сложно разобрать, если вы еще не понимаете, что он пытается разрешить или запретить. Понимание acq / rel с точки зрения односторонних барьеров - это поэтому намного проще, чем то, что говорится в стандарте об установлении отношений "синхронизируется с". Это имеет смысл, когда вы его осваиваете, но ИМО, понимание, ориентированное на HW, является полезным строительным блоком для чтения стандарта. Статьи Джеффа Прешинга представляют собой хорошее сочетание абстрактного уровня C ++ с HW-ориентированным мышлением, IMO. - person Peter Cordes; 30.11.2019

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

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

Жизненный цикл записи начинается в механизме неисправности, где он все еще является спекулятивным (то есть - может быть очищен из-за неправильного предсказания или ошибки более старого перехода). Естественно, в это время запись снаружи не видна, поэтому исполнение вне очереди здесь не актуально. После фиксации, если система гарантирует порядок хранения (например, x86), ей все равно придется ждать в очереди, чтобы ее очередь стала видимой, поэтому она буферизуется. Другие ядра не могут его видеть, поскольку время его наблюдения еще не достигнуто (хотя локальные нагрузки в этом ядре могут видеть его в некоторых реализациях x86 - это одно из различий между TSO и реальной последовательной согласованностью). После того, как старые хранилища будут завершены, хранилище может стать глобально видимым - для этого ему не нужно выходить куда-либо за пределы ядра, он может оставаться кешированным внутри. Фактически, некоторые процессоры могут даже сделать его наблюдаемым, пока он все еще находится в буфере хранилища, или спекулятивно записать его в кеш - фактическая точка принятия решения - когда заставить его реагировать на внешние слежения, остальное - это детали реализации. Архитектуры с более расслабленным порядком могут изменить порядок, если он явно не заблокирован забором / барьером.

Исходя из этого, ваш фрагмент кода не может переупорядочивать магазины на x86, поскольку магазины не переупорядочиваются друг с другом там, но он может сделать это, например, на руке. Если в этом случае язык требует строгого упорядочивания, компилятор должен будет решить, может ли он полагаться на HW или добавить ограждение. В любом случае, любой, кто читает это значение из другого потока (или сокета), должен будет отслеживать его и может видеть только те записи, которые отвечают.

person Leeor    schedule 17.05.2015
comment
Интересный факт: некоторые ISA в теории и POWER на практике позволяют хранилищу стать видимым для некоторых других потоков, прежде чем они будут зафиксированы в согласованном кэше L1d и станут видимыми глобально. Механизм представляет собой переадресацию устаревших (т. Е. Неспекулятивных) хранилищ между логическими ядрами одного и того же физического ядра SMT. Будут ли две атомарные записи в разные места в разных потоках всегда отображаться в одном порядке другими потоками?. ISO C ++ допускает переупорядочивание / несогласованность IRIW для mo_relaxed, но не mo_seq_cst - person Peter Cordes; 30.11.2019