Предотвращает ли изменчивость, характерная для MS, переупорядочивание аппаратных инструкций

Из документации:

Специально для Microsoft

Когда используется параметр компилятора /volatile:ms — по умолчанию, когда нацелены архитектуры, отличные от ARM, — компилятор генерирует дополнительный код для поддержания порядка ссылок на изменчивые объекты в дополнение к поддержанию порядка ссылок на другие глобальные объекты. В частности:

  • Запись в изменчивый объект (также известная как изменчивая запись) имеет семантику Release; то есть ссылка на глобальный или статический объект
    , которая возникает перед записью в изменчивый объект в последовательности инструкций
    , будет происходить до этой изменчивой записи в скомпилированном
    двоичном файле.
  • Чтение изменчивого объекта (также известное как изменчивое чтение) имеет семантику Acquire; то есть ссылка на глобальный или статический объект
    , возникающая после чтения энергозависимой памяти в последовательности инструкций
    , будет происходить после этого чтения энергозависимой памяти в скомпилированном двоичном файле.

Это позволяет использовать энергозависимые объекты для блокировки и освобождения памяти в многопоточных приложениях.

Это, безусловно, гарантирует, что volatile не позволит компилятору выполнять переупорядочивание инструкций во время компиляции (поскольку в нем явно указано, что последовательность инструкций будет такой же в скомпилированном двоичном файле).

Но, как мы все знаем, есть еще такая вещь, как аппаратное переупорядочивание (например, ЦП может переупорядочивать инструкции по собственной воле). volatile также препятствует этому? Я знаю, что это делают примитивы синхронизации (например, мьютексы), но как насчет специфичных для MS volatile?


person FrozenHeart    schedule 11.08.2016    source источник
comment
Под архитектурами, отличными от ARM, MS, вероятно, подразумевает x86 и x64. В x86 и x64 загрузки не переупорядочиваются с другими загрузками. И магазины не заказываются с другими магазинами. (с некоторыми исключениями, такими как невременные хранилища) IOW, пока компилятор ничего не переупорядочивает, процессор тоже. Похоже, что /volatile для MS пытается обеспечить функциональность std::atomic, которой не было до C++11.   -  person Mysticial    schedule 11.08.2016
comment
Кроме того, они говорят, что имеет семантику Release, поэтому документация, безусловно, утверждает volatile, что предотвращает переупорядочивание ЦП.   -  person Martin Bonner supports Monica    schedule 11.08.2016
comment
@Mysticial Похоже, /volatile для MS пытается обеспечить функциональность std::atomic - нет, это не так. Например, не гарантируется, что операции чтения и записи volatile переменных являются атомарными. На самом деле это так, потому что x86 гарантирует атомарность операций чтения и записи для 1-байтовых переменных, но в MSDN это не гарантируется volatile.   -  person FrozenHeart    schedule 11.08.2016
comment
@FrozenHeart Извините, я должен был немного уточнить. Он пытается обеспечить функциональность std::memory_order_xxx, которую использует std::atomic. Хотя семантика приобретения/выпуска для чего-то, что не является атомарным, кажется довольно э... ненадежным.   -  person Mysticial    schedule 11.08.2016
comment
@Mysticial Ну да, конечно. Но вы уверены в переупорядочении оборудования? Если да, то почему вы не написали ответ на мой вопрос?   -  person FrozenHeart    schedule 11.08.2016
comment
Почему бы просто не использовать стандартные конструкции, где вы знаете, что такое гарантии, и перестать возиться с платформозависимой ерундой? Ваш код будет чище, мобильнее и понятнее вашим коллегам-разработчикам.   -  person Jesper Juhl    schedule 11.08.2016
comment
Предполагая, что ваши коллеги-разработчики знакомы с расширениями Microsoft не лучше, чем с относительно новыми дополнениями стандартной библиотеки. :-) Это, безусловно, верно по моему опыту. Интуитивная реакция Stack Overflow на все, что недостаточно переносимо, утомляет. Приложения x86 для Windows никогда не станут переносимыми, поэтому вы просто используете правильные инструменты для работы, которые создают разумный и понятный код.   -  person Cody Gray    schedule 11.08.2016
comment
@Cody: Но читай между строк. Microsoft продолжает поддерживать эту снижающую производительность версию volatile для обратной совместимости. Если вы обнаружите, что он работает не так, как вы ожидаете, при наличии новых функций, таких как лямбда-выражения, при подаче отчета об ошибке вы, вполне возможно, услышите, что вы пишете новый код, ошибка не влияет на существующий код. код, и мы уже сказали вам использовать режим /volatile:iso в новом коде   -  person Ben Voigt    schedule 11.08.2016
comment
@ben Это здорово, если вы пишете новое приложение с нуля. Что вы делаете с существующими кодовыми базами? Вы не можете просто начать писать новые биты, используя стандартные конструкции ISO, и щелкнуть переключателем /volatile:iso, потому что это может сломать старый код. Эти два варианта плохо сочетаются. Поэтому я сомневаюсь, что вы услышите такой ответ. Хотя я не знаю, позиция Microsoft в последнее время меня очень удивила.   -  person Cody Gray    schedule 11.08.2016
comment
@CodyGray: я лично не пробовал связывать единицы компиляции с использованием двух разных настроек, но я не вижу ничего, что не сочеталось бы хорошо. Таким образом, ответ на существующий код заключается в том, что вы можете написать новый код, используя новые настройки, и использовать компоновщик, чтобы объединить его со старым кодом.   -  person Ben Voigt    schedule 11.08.2016


Ответы (1)


Документы MSDN о изменчивом поведении MS восходят к VS2003. Так что это было уже давно — задолго до появления std::atomic в C++11.

Таким образом, специфичная для MS volatile, по-видимому, является способом достижения семантики получения/освобождения в старые времена. Но теперь он в основном устарел, и они оставили сноску, подталкивающую вас от MS-volatile в пользу std::atomic и /volatile:iso для межпотокового взаимодействия.


Что касается того, почему они исключают ARM, Microsoft не использовала ARM до относительно недавнего времени. Помимо ARM, они поддерживают x86, x64 и Itanium (который мертв).

В x86 и x64 большинство операций загрузки и сохранения уже имеют семантику получения/освобождения (за исключением таких случаев, как невременное сохранение). Таким образом, пока компилятор ничего не переупорядочивает, процессор также не будет * и, следовательно, сохранит семантику захвата/освобождения. Флаг /volatile:ms указывает компилятору не переупорядочивать что-либо, чтобы семантика захвата/освобождения могла быть достигнута на x86 и x64.

Поскольку поддержка Microsoft ARM является относительно новой, а специфичная для MS переменная (/volatile:ms) устарела в пользу std::atomic, они, вероятно, решили отказаться от классической семантики volatile, а не обновлять их для работы с ARM (что, вероятно, означало бы добавление барьеров памяти). везде, учитывая отсутствие аппаратной поддержки).

*Процессор по-прежнему будет выполнять любое переупорядочивание, но он сохранит семантику захвата/освобождения программы, поскольку это требуется для x86/x64. (за исключением исключительных случаев, таких как nt-stores или clflush). Как это сделать, не нарушая порядок памяти, — это отдельная тема.

person Mysticial    schedule 11.08.2016
comment
Семантика ms-volatile позволяет гарантировать упорядочение объектов, доступных через обычные указатели. Если я заполню буфер данными, используя неполные указатели, а затем сохраню его адрес в volatile * volatile, дождусь очистки этого указателя, а затем сделаю что-то еще с буфером, используя неполные указатели, первый набор записей будет будет выполнено до сохранения volatile, а второй набор операций записи не будет выполнен до тех пор, пока указатель не будет очищен. - person supercat; 13.03.2019
comment
Есть ли способ добиться аналогичной семантики с использованием атомарных значений, не требуя, чтобы код, который записывает буфер, использовал для этого указатели с атомарными параметрами? - person supercat; 13.03.2019
comment
@supercat Поскольку сам указатель является переменной синхронизации, сделайте указатель std::atomic. Как только вы закончили писать в первый раз, вы устанавливаете указатель с магазином-релизом. Пока вы ждете, вы выполняете загрузку указателя, пока он не будет очищен. Затем вы можете сделать 2-й набор записей. Получение загрузки предотвратит перемещение 2-го набора записей перед загрузкой, поэтому 2-й набор записей может произойти только после очистки указателя. - person Mysticial; 13.03.2019
comment
Подождите, кажется, я неправильно понял ваш комментарий. Вы имели в виду без создания атомарного указателя? Я не уверен, что это возможно без использования какой-либо другой формы синхронизации. - person Mysticial; 13.03.2019
comment
Проблема в том, что семантика получения и освобождения, определенная Стандартом, позволяет управлять порядком только в отношении atomic-квалифицированных объектов. Семантика ms-volatile гарантирует, что запись-освобождение будет заказано после всех предшествующих операций, включающих хранение — атомарное или нет, — которое не принадлежит неутекшим указателям с указанием restrict, а также загрузка-получение будет заказана перед всеми последующими операциями, связанными с таким хранением. - person supercat; 13.03.2019
comment
В некоторых средах выполнения при задании чего-то вроде extern int q; extern int volatile * volatile v; ... q=1; q=2; v=&q; while(v) {} q=3; эффект q=2; никогда нельзя было наблюдать, и эффективность можно было бы повысить, позволив компилятору пропустить его. Однако в других средах действие сохранения &q в v может сделать эффекты q=2; наблюдаемыми, даже если эффект p=1; не будет. Я думаю, что авторы C89 ожидали, что люди, пишущие реализации для сред, где операции volatile могут делать различные другие действия наблюдаемыми... - person supercat; 13.03.2019
comment
... будет определять свою семантику volatile, чтобы приспособиться к этому (или иметь возможность настройки для этого), а не требовать использования нестандартного синтаксиса для таких целей. Я не уверен, откуда люди взяли идею о том, что Стандарт был задуман как приглашение разработчикам компиляторов игнорировать дух C, который Комитет частично охарактеризовал как «Не мешай программисту делать то, что нужно». - person supercat; 13.03.2019
comment
Я думаю, что мотивация заключается в том, что семантика volatile класса для упорядочения памяти не может быть эффективно реализована на слабых архитектурах упорядочения памяти, таких как ARM. Таким образом, модель памяти C++11 разработана с учетом этого. IOW, по умолчанию порядок не гарантируется, и вы должны явно указать точки синхронизации. Точно так же любой порядок неатомарных элементов не имеет значения, потому что, если бы он имел место, это было бы условием гонки чтения/записи, которое автоматически является UB. - person Mysticial; 13.03.2019
comment
Программисты, использующие автономные реализации, обычно знают о целевой среде то, чего не знают (а во многих случаях и не могут) авторы компиляторов; большинство задач, для которых используются автономные реализации, были бы невозможны без таких знаний. Если программисту нужно, чтобы компилятор выполнял некоторые операции сохранения перед определенным хранилищем ключей, программист должен иметь возможность требовать такого поведения, не прыгая через обручи, независимо от того, знает ли автор компилятора, будет ли это полезно в целевой среде. . - person supercat; 13.03.2019