Что строка в команде volatile указывает на [duplicate]

Я отвечу ужасным, нарисованным рукой комиком. Второе изображение является причиной того, что result является undefined в вашем примере кода.

33
задан Deduplicator 20 October 2014 в 00:33
поделиться

3 ответа

Модификатор __volatile__ в блоке __asm__ заставляет оптимизатор компилятора выполнять код как есть. Без него оптимизатор может подумать, что он может быть удален или удален из цикла и кэширован.

Это полезно для команды rdtsc, например:

__asm__ __volatile__("rdtsc": "=a" (a), "=d" (d) )

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

При использовании в одиночку, например:

__asm__ __volatile__ ("")

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

__asm__ __volatile__ ("":::"memory")

Инструкция rdtsc является хорошим примером для volatile. rdtsc обычно используется, когда вам нужно время, какое количество команд требуется выполнить. Представьте себе такой код, в котором вы хотите выполнить время r1 и r2:

__asm__ ("rdtsc": "=a" (a0), "=d" (d0) )
r1 = x1 + y1;
__asm__ ("rdtsc": "=a" (a1), "=d" (d1) )
r2 = x2 + y2;
__asm__ ("rdtsc": "=a" (a2), "=d" (d2) )

Здесь компилятору разрешено кэшировать метку времени, а допустимый вывод может показать, что каждый линия заняла ровно 0 тактов. Очевидно, это не то, что вы хотите, поэтому вы вводите __volatile__ для предотвращения кеширования:

__asm__ __volatile__("rdtsc": "=a" (a0), "=d" (d0))
r1 = x1 + y1;
__asm__ __volatile__("rdtsc": "=a" (a1), "=d" (d1))
r2 = x2 + y2;
__asm__ __volatile__("rdtsc": "=a" (a2), "=d" (d2))

Теперь вы будете получать новую временную метку каждый раз, но у нее все еще есть проблема, что и компилятор и процессору разрешено изменять порядок всех этих операторов. Это может привести к выполнению блоков asm после того, как r1 и r2 уже были рассчитаны. Чтобы обойти это, вы добавили бы некоторые барьеры, которые вызывают сериализацию:

__asm__ __volatile__("mfence;rdtsc": "=a" (a0), "=d" (d0) :: "memory")
r1 = x1 + y1;
__asm__ __volatile__("mfence;rdtsc": "=a" (a1), "=d" (d1) :: "memory")
r2 = x2 + y2;
__asm__ __volatile__("mfence;rdtsc": "=a" (a2), "=d" (d2) :: "memory")

Обратите внимание на инструкцию mfence здесь, которая обеспечивает соблюдение барьера на стороне процессора, а спецификатор «памяти» в изменчивой блок, который обеспечивает компромисс-барьер. На современных процессорах вы можете заменить mfence:rdtsc на rdtscp для чего-то более эффективного.

50
ответ дан Cory Nelson 26 August 2018 в 13:03
поделиться

Атрибут __asm__ указывает имя, которое будет использоваться в коде ассемблера для функции или переменной.

Квалификатор __volatile__, обычно используемый в системах реального времени для встроенных систем, решает проблему с испытаниями компилятора status register для бит ERROR или READY, вызывающим проблемы при оптимизации , __volatile__ был представлен как способ сообщить компилятору, что объект подвержен быстрым изменениям, и заставить каждую ссылку объекта быть подлинной ссылкой.

-1
ответ дан David C. Rankin 26 August 2018 в 13:03
поделиться

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

int a = 2;
asm("mov a, 3");
printf("%i", a); // will print 3

Компиляторы имеют разные варианты. __asm__ должен быть синонимом, возможно, с некоторыми различиями в компиляторах.

volatile означает, что переменная может быть изменена извне (ака не программой C). Например, при программировании микроконтроллера, где адрес памяти 0x0000x1234 отображается на каком-либо интерфейсе устройства (например, при кодировании для GameBoy, к кнопкам / экрану / и т. Д.)

volatile std::uint8_t* const button1 = 0x00001111;

Эти отключенные оптимизаторы компилятора, которые полагаются на *button1, не меняются, если они не изменяются кодом.

Он также используется в многопоточном программировании (не требуется больше сегодня?), Где переменная может быть изменена другим thread.

inline - это подсказка для компилятора для «встроенных» вызовов функции.

inline int f(int a) {
    return a + 1
}

int a;
int b = f(a);

Это не должно быть скомпилировано в вызов функции f но в int b = a + 1. Как будто f, где макрос. Компиляторы в основном делают эту оптимизацию автоматически в зависимости от использования / содержимого функции. __inline__ в этом примере может иметь более конкретное значение.

Аналогично __attribute__((noinline)) (синтаксис, специфичный для GCC) предотвращает встраивание функции.

2
ответ дан tmlen 26 August 2018 в 13:03
поделиться
Другие вопросы по тегам:

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