Какие виды оптимизаций предотвращает «volatile» в C ++?

Я искал ключевое слово volatile и его назначение, и получил примерно такой ответ:

Оно используется для предотвращения оптимизации кода компилятором.

Были некоторые из них. например, при опросе оборудования с отображением в память: без volatile цикл опроса будет удален, поскольку компилятор может распознать, что значение условия никогда не изменяется. Но поскольку было всего один или, может быть, два примера, это заставило меня задуматься: есть ли другие ситуации, когда нам нужно использовать volatile , чтобы избежать нежелательной оптимизации? Являются ли условные переменные единственным местом, где требуется volatile ?

Я полагаю, что оптимизация зависит от компилятора и поэтому не указана в спецификации C ++. Означает ли это, что мы должны руководствоваться интуицией, говоря Хм, я подозреваю, что мой компилятор покончит с этим, если я не объявлю эту переменную как volatile или есть какие-то четкие правила?

25
задан gablin 30 August 2010 в 22:02
поделиться

5 ответов

По сути, volatile сообщает, что значение может измениться за спиной вашей программы. Это не позволяет компиляторам кэшировать значение (в регистре ЦП) и оптимизировать доступ к этому значению, когда они кажутся ненужными с точки зрения вашей программы.

Что должно вызвать использование volatile, так это когда значение изменяется, несмотря на то, что ваша программа не записывала его, и когда нет других барьеров памяти (таких как мьютексы, используемые для многопоточных программ). присутствуют.

25
ответ дан 28 November 2019 в 18:15
поделиться

Переменные условия не, где требуется volatile; строго это необходимо только в драйверах устройств.

volatile гарантирует, что операции чтения и записи в объект не будут оптимизированы или переупорядочены по отношению к другому volatile. Если вы зацикливаете переменную, измененную другим потоком, ее следует объявить volatile. Однако вы не должны зацикливаться. Поскольку язык на самом деле не был разработан для многопоточности, он не очень хорошо поддерживается. Например, компилятор может переместить запись в не-изменчивую переменную из положения после в начало цикла, нарушив блокировку. (Для неопределенных спин-циклов это может произойти только в C++0x.)

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

C++0x может и не иметь этого недостатка, так как он вводит формальную семантику многопоточности. Я не совсем знаком с изменениями, но ради обратной совместимости не требуется объявлять что-либо изменчивое, чего не было раньше.

10
ответ дан 28 November 2019 в 18:15
поделиться

Один из способов представить изменчивую переменную — представить, что это виртуальное свойство; записи и даже чтения могут делать вещи, о которых компилятор не может знать.Фактически сгенерированный код для записи/чтения изменчивой переменной представляет собой просто запись в память или чтение (*), но компилятор должен рассматривать этот код как непрозрачный; он не может делать никаких предположений, при которых он мог бы быть излишним. Проблема заключается не только в том, чтобы убедиться, что скомпилированный код замечает, что что-то вызвало изменение переменной. В некоторых системах даже чтение памяти может «делать» что-то.

(*) В некоторых компиляторах переменные volatile могут добавляться, вычитаться, увеличиваться, уменьшаться и т. д. как отдельные операции. Компилятору, вероятно, полезно скомпилировать:

  volatilevar++;

как

  inc [_volatilevar]

, поскольку последняя форма может быть атомарной на многих микропроцессорах (но не на современных многоядерных ПК). Однако важно отметить, что если бы утверждение было:

  volatilevar2 = (volatilevar1++);

, правильный код был бы не

  mov ax,[_volatilevar1] ; Reads it once
  inc [_volatilevar]     ; Reads it again (oops)
  mov [_volatilevar2],ax

и не

  mov ax,[_volatilevar1]
  mov [_volatilevar2],ax ; Writes in wrong sequence
  inc ax
  mov [_volatilevar1],ax

, а скорее

  mov ax,[_volatilevar1]
  mov bx,ax
  inc ax
  mov [_volatilevar1],ax
  mov [_volatilevar2],bx

. безопасный) код. Если бы volatilevar1 не возражала против того, чтобы ее читали дважды, а volatilevar2 не возражала против того, чтобы ее записывали перед volatilevar1, то разделение оператора на

  volatilevar2 = volatilevar1;
  volatilevar1++;

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

1
ответ дан 28 November 2019 в 18:15
поделиться

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

4
ответ дан 28 November 2019 в 18:15
поделиться

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

Оно может находиться в регистре:

Его значение может быть вычислено, например. in:

int x = 2;
int y = x + 7;
return y + 1;

Не обязательно иметь x и y, но можно просто заменить на:

return 10;

И еще один пример: любой код, который не влияет на состояние извне может быть удалено полностью. Например. если вы обнулите конфиденциальные данные, компилятор может счесть это бесполезным занятием («почему вы пишете то, что не будет прочитано?») и удалить его. volatile можно использовать, чтобы остановить это.

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

(Примечание C#.Многое, что я видел в последнее время на volatile, предполагает, что люди читают о C++ volatile и применяют его к C#, читают об этом в C# и применяют его к C++. Однако на самом деле volatile ведет себя настолько по-разному между ними, что бесполезно считать их связанными).

4
ответ дан 28 November 2019 в 18:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: