Как мне узнать, согласен ли gcc, что что-то изменчиво? - PullRequest
8 голосов
/ 13 марта 2009

Рассмотрим следующее:

volatile uint32_t i;

Как узнать, рассматривал ли gcc меня как изменчивый? Это будет объявлено как таковое, потому что ни один соседний код не изменит его, и изменение может произойти из-за некоторого прерывания.

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

Если вы берете следующий глупый код:

#include <stdio.h>
#include <inttypes.h>

volatile uint32_t i;

int main(void)
{
        if (i == 64738)
                return 0;
        else
                return 1;
}

Скомпилируйте его в объектный формат и разберите его с помощью objdump, затем сделайте то же самое после удаления 'volatile', разницы нет (согласно diff). Является ли декларация volatile слишком близкой к тому месту, где она проверена или изменена, или мне всегда следует использовать какой-то атомарный тип при объявлении чего-то изменчивого? Влияют ли на это некоторые флаги оптимизации?

Обратите внимание, мой дурацкий пример не полностью соответствует моему вопросу, я это понимаю. Я только пытаюсь выяснить, рассматривал ли gcc переменную как переменную или нет, поэтому я изучаю небольшие дампы, чтобы попытаться найти разницу.

Ответы [ 7 ]

14 голосов
/ 13 марта 2009

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

Чтобы быть на 100% уверенным, и для такого простого примера посмотрите вывод сборки.

12 голосов
/ 13 марта 2009

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

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

6 голосов
/ 13 марта 2009

Насколько я знаю, volatile помогает оптимизатору. Например, если ваш код выглядел так:

int foo() {
    int x = 0;
    while (x);
    return 42;
}

Цикл while будет оптимизирован вне двоичного кода.

Но если вы определите 'x' как энергозависимый (т. Е. volatile int x;), то компилятор оставит цикл в покое.

5 голосов
/ 13 марта 2009

Ваш маленький образец не подходит, чтобы показать что-либо. Разница между переменной volatile и переменной, которая отсутствует, заключается в том, что каждая загрузка или хранилище в коде должно генерировать ровно одну загрузку или сохранение в исполняемом файле для volatile переменной, тогда как компилятор может оптимизировать удаленные загрузки или хранилища переменные Если вы получаете одну загрузку i в своем образце, это то, что я ожидаю от энергозависимых и энергонезависимых.

Чтобы показать разницу, вам понадобятся избыточные загрузки и / или хранилища. Попробуйте что-то вроде

int i = 5;
int j = i + 2;
i = 5;
i = 5;
printf("%d %d\n", i, j);

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

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

4 голосов
/ 13 марта 2009

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

4 голосов
/ 13 марта 2009

Он должен всегда рассматривать как изменчивый.

Причина, по которой код такой же, заключается в том, что volatile просто указывает компилятору загружать переменную из памяти каждый раз, когда она обращается к ней. Даже при включенной оптимизации компилятору все равно нужно один раз загрузить i из памяти в коде, который вы написали, потому что он не может определить значение i во время компиляции. Если вы получите к нему доступ несколько раз, вы увидите разницу.

4 голосов
/ 13 марта 2009

Прочитайте стандарт, прежде чем неправильно цитировать или понижать голос. Вот цитата из n2798:

7.1.6.1 Cv-квалификаторы

7 Примечание: volatile - это подсказка для реализации, чтобы избежать агрессивной оптимизации, связанной с объектом, поскольку значение объекта может быть изменено средствами, которые не могут быть обнаружены реализацией. См. 1.9 для подробной семантики. В общем, семантика volatile должна быть такой же в C ++, как и в C.

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

Поскольку путаницы так много, еще немного. Стандарт C99 фактически говорит о том, что изменяемый квалифицированный объект должен проверяться каждый раз, когда он читается, и так далее, как отметили другие. Но есть и другой раздел, в котором говорится, что то, что составляет изменчивый доступ, определяется реализацией. Таким образом, компилятор, который знает аппаратное обеспечение наизнанку, будет знать, например, когда у вас есть автоматически изменяемая квалифицированная переменная и адрес которой никогда не берется, что она не будет помещена в чувствительную область памяти и почти наверняка проигнорирует намек и оптимизировать его.

Это ключевое слово используется в типах обработки ошибок setjmp и longjmp. Единственное, что вы должны иметь в виду: вы задаете ключевое слово volatile, когда думаете, что переменная может измениться. То есть вы можете взять обычный объект и управлять им несколькими кастами.

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

Если вы действительно хотели другую сборку сборки с оптимизацией

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...