изменчивые переменные и атомарные операции в Visual C++ x86

StackOverflow https://stackoverflow.com/questions/4976703

Вопрос

Обычная загрузка имеет семантику получения на x86, обычное хранилище имеет семантику выпуска, однако компилятор все еще может изменять порядок инструкций.В то время как ограждения и заблокированные инструкции (locked xchg, locked cmpxchg) предотвращают изменение порядка как аппаратного обеспечения, так и компилятора, простые загрузки и хранилища по-прежнему необходимо защищать с помощью барьеров компилятора.Visual C++ предоставляет функцию _ReadWriterBarrier(), которая предотвращает переупорядочивание компилятором, также C++ предоставляет ключевое слово volatile по той же причине.Я пишу всю эту информацию просто для того, чтобы убедиться, что все понимаю правильно.Итак, все написанное выше верно, есть ли какая-либо причина помечать как изменчивые переменные, которые будут использоваться в функциях, защищенных _ReadWriteBarrier()?

Например:

int load(int& var)
{
    _ReadWriteBarrier();
    T value = var;
    _ReadWriteBarrier();
    return value;
}

Безопасно ли сделать эту переменную энергонезависимой?Насколько я понимаю, это так, потому что функция защищена, и компилятор внутри не может выполнить переупорядочение.С другой стороны, Visual C++ обеспечивает особое поведение для изменчивых переменных (отличное от того, что делает стандарт), он выполняет изменчивые операции чтения и записи, атомарные загрузки и хранилища, но моя цель - x86, а обычные загрузки и хранилища в любом случае должны быть атомарными на x86, верно?

Заранее спасибо.

Это было полезно?

Решение

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

Так что это последний сценарий, где волатильный можно считать, что _readwriteBarrier.

_readwritebarrier - это не функция - _readwritebarrier не вставляет никаких дополнительных инструкций, и она не помешает процессору от перестановки читателей и пишет - он только предотвращает их перенаправить компилятору. _Readwritebarrier - предотвратить переупорядочение компилятора.

memorybarrier - предотвратить переупорядочение CPU!

Компилятор, как правило, переставляет инструкции ... C ++ не содержит встроенной поддержки для многопоточных программ, чтобы компилятор предполагал, что код является однопоточным при переупорядочении кода. С помощью MSVC используйте _readwritebarrier в коде, чтобы компилятор не переместил чтение и пишет через него.

Проверьте эту ссылку для более подробного обсуждения по темам http://msdn.microsoft.com/ru-- US / Библиотека / EE418650 (v= vs.85) .aspx

Относительно вашего фрагмента кода - вам не нужно использовать readwritebarrier как примитив синхронизации - первый звонок для _readwritebarrier не нужен.

При использовании readwritebarrier вам не нужно использовать волатильный

Вы написали «Это делает волатильный читать и пишет атомные нагрузки и магазины» - я не думаю, что это нормально, чтобы сказать, что атомальность и волатильность разные. Атомные операции считаются неделимыми - ... http://www.yoda. arachsys.com/csharp/threads/Volatility.shtml

Другие советы

Примечание:Я не являюсь экспертом в этой теме, некоторые мои высказывания являются "то, что я услышал в Интернете", но я думаю, что все же смогу прояснить некоторые неправильные представления.

[править] В общем, я бы полагался на специфику платформы, такую как атомарные чтения x86 и отсутствие OOOX, только в изолированных локальных оптимизациях, которые защищены #ifdef проверка целевой платформы, в идеале сопровождаемая портативным решением в #else путь.

На что стоит обратить внимание

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

Возможные неправильные представления

1. Насколько я понимаю, это так, потому что функция защищена, и компилятор внутри не может выполнить переупорядочение.
[править] Чтобы прояснить:то _ReadWriteBarrier обеспечивает защиту от переупорядочивания команд, однако вы должны выходить за рамки этой функции. _ReadWriteBarrier было исправлено в VS 2010, чтобы сделать это, более ранние версии могут быть повреждены (в зависимости от оптимизаций, которые они на самом деле выполняют).

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

2. Visual C++ [...] выполняет энергозависимые операции чтения и записи, атомарные загрузки и сохранения,
Где ты это нашел? MSDN говорит, что за пределами стандарта будут установлены барьеры памяти для чтения и записи, нет гарантии для атомарного чтения.

[править] Обратите внимание, что C#, Java, Delphi и т.д.имеют разные уровни памяти и могут давать разные гарантии.

3. обычные загрузки и хранилища в любом случае должны быть атомарными на x86, верно?
Нет, это не так.Невыровненные чтения не являются атомарными.Они случилось так, что атомарные, если они хорошо выровнены - факт, на который я бы не стал полагаться, если только он не изолирован и им легко обмениваться.В противном случае ваше "упрощение для x86" становится ограничением доступа к этой цели.

[править] Происходит невыровненное чтение:

char * c = new char[sizeof(int)+1];
load(*(int *)c);      // allowed by standard to be unaligned
load(*(int *)(c+1));  // unaligned with most allocators

#pragma pack(push,1)
struct 
{
   char c;
   int  i;
} foo;
load(foo.i);         // caller said so
#pragma pack(pop)

Это, конечно, все академично, если вы помните, что параметр должен быть выровнен, и вы контролируете весь код.Я бы больше не стал писать такой код, потому что в прошлом меня часто одолевала лень.

4. Обычная загрузка имеет семантику получения на x86, обычное хранилище имеет семантику выпуска
Нет.процессоры x86 не используют выполнение не по порядку (или, скорее, нет видимого OOOX - я думаю), но это не мешает оптимизатору переупорядочивать инструкции.

5. _ReadBarrier / _WriteBarrier / _ReadWriteBarrier выполняют всю магию Они этого не делают - они просто предотвращают изменение порядка оптимизатором.MSDN наконец-то превратил его в большое плохое предупреждение для VS2010, но эта информация, по-видимому, применима к предыдущие версии также.


Теперь перейдем к вашему вопросу.

Я предполагаю, что цель фрагмента состоит в том, чтобы передать любую переменную N и загрузить ее (атомарно?) Простым выбором было бы блокированное чтение или (в Visual C++ 2005 и более поздних версиях) изменчивое чтение.

В противном случае вам понадобился бы барьер как для компилятора, так и для процессора перед чтением, в VC++ parlor это было бы:

int load(int& var)
{   
  // force Optimizer to complete all memory writes:
  // (Note that this had issues before VC++ 2010)
   _WriteBarrier();    

  // force CPU to settle all pending read/writes, and not to start new ones:
   MemoryBarrier();

   // now, read.
   int value = var;    
   return value;
}

Нет, это _WriteBarrier есть второе предупреждение в MSDN:*В прошлых версиях компилятора Visual C++ функции _ReadWriteBarrier и _writebarrier_writebarrier применялись только локально и не влияли на функции, расположенные выше по дереву вызовов.Эти функции теперь применяются на всем пути вверх по дереву вызовов.*


Я надеяться это правильно.stackoverflowers, пожалуйста, поправьте меня, если я ошибаюсь.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top