Почему добавление к переменной квалификатора volatile не препятствует изменению порядка инструкций?

У меня есть простой фрагмент кода C++, как показано ниже:

int A;
int B;

void foo() {
    A = B + 1;
    // asm volatile("" ::: "memory");
    B = 0;
}

Когда я компилирую этот код, сгенерированный код сборки переупорядочивается следующим образом:

foo():
        mov     eax, DWORD PTR B[rip]
        mov     DWORD PTR B[rip], 0
        add     eax, 1
        mov     DWORD PTR A[rip], eax
        ret
B:
        .zero   4
A:
        .zero   4

Однако, когда я добавляю ограничение памяти (строка с комментариями в коде C++), инструкции не переупорядочиваются. Насколько я понимаю, добавление квалификатора volatile к переменной также должно предотвратить изменение порядка инструкций. Итак, я изменил код, чтобы добавить volatile к переменной B:

int A;
volatile int B;

void foo() {
    A = B + 1;
    B = 0;
}

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

Код доступен в godbolt

asm volatile ... — это специфичное для компилятора расширение, которое полностью отличается от стандартного квалификатора C++ volatile .
Some programmer dude 27.06.2023 15:09

Не уверен, о чем ваш вопрос, но вы только отметили B volatile, и доступ к нему не переупорядочивается (сначала читайте, ноль секунд). Если вы хотите, чтобы запись в A была заказана, вам также нужно отметить ее как изменчивую.

Jester 27.06.2023 15:11

Кроме того, то, что происходит и какой ассемблерный код может быть сгенерирован, во многом зависит от компилятора, версии компилятора и флагов оптимизации. Пожалуйста, включите все эти детали в сам вопрос. А также, пожалуйста, сообщите нам, на каком языке вы действительно пишете свой код. Изначально вы пометили C++11 (который я отредактировал на C++), но в самом вопросе вы упоминаете только C. C и C++ - два очень разных языка.

Some programmer dude 27.06.2023 15:13

@Someprogrammerdude Вы могли видеть ссылку, которую я разместил, там есть вся необходимая информация godbolt

tang 27.06.2023 15:27

@Шут Ах! Я полагаю, что неправильно понял значение «переупорядочить». Если переменная является изменчивой, это означает, что любой доступ (чтение и запись) к ней не будет изменен. Первоначально я думал, что это означает, что предложения кода не будут переупорядочены.

tang 27.06.2023 15:30

вся необходимая информация должна быть в вопросе, а не за ссылками на внешние сайты

463035818_is_not_an_ai 27.06.2023 15:33

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

Some programmer dude 27.06.2023 15:33

ссылка godbolt на C++, так почему вы говорите о C в вопросе? О каком из них идет речь?

463035818_is_not_an_ai 27.06.2023 15:33

@Someprogrammerdude Приношу извинения за неудобства, вызванные размещением внешней ссылки. В будущем я позабочусь о том, чтобы мои вопросы и ответы были автономными.

tang 27.06.2023 15:46

@ 463035818_is_not_an_ai Извинения за путаницу в вопросе. Я уже исправил нестыковку, и вопрос действительно про C++

tang 27.06.2023 15:50

Доступы volatile не переупорядочиваются с любыми другими доступами volatile, в том числе к другим местоположениям, поэтому изменение обеих переменных даст вам ожидаемый asm. Но не используйте atomic<T> с memory_order_relaxed вместо этого, если другой поток будет обращаться к этим глобальным переменным. (Когда использовать volatile с многопоточностью?)

Peter Cordes 27.06.2023 18:30

Спасибо за ваш ответ @PeterCordes. Я сделал некоторые выводы относительно использования volatile. Вы можете найти их в разделе комментариев ответа Нейта Элдриджа. Пожалуйста, не стесняйтесь проверить их.

tang 27.06.2023 18:38
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
12
58
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Насколько я понимаю, добавление квалификатора volatile к переменной также должно предотвратить переупорядочивание инструкций.

Это большое упрощение. Хотя стандарт C++ не очень явно определяет семантику volatile (говоря только, что «доступ оценивается строго в соответствии с правилами абстрактной машины»), неписаное правило заключается в том, что volatile объекты обрабатываются так, как если бы некий внешний объект (например, аппаратное обеспечение ввода-вывода) может считывать и записывать их асинхронно, и что и чтение, и запись являются побочными эффектами, которые может наблюдать внешний объект. Таким образом, каждое чтение/запись объекта volatile (размером машинного слова или меньше) должно приводить к выполнению ровно одной инструкции загрузки/сохранения.

Из этого следует, что объекты загрузки и сохранения в volatile не будут переупорядочиваться друг с другом. Но в вашей программе A не является volatile, поэтому будем считать, что внешняя сущность его не видит. Следовательно, не имеет значения, как упорядочены обращения к A по отношению к обращениям к B или чему-то еще, и компилятор может изменить их порядок. Такие инструкции, как add eax, 1, которые вообще не обращаются к памяти, также являются честной игрой; внешний объект также не может видеть регистры машины.

Судя по вашему использованию тега concurrency , это одна из многих причин, по которой volatile не является правильным подходом к совместному использованию переменных между потоками, потому что, в отличие от «внешнего объекта», другой поток имеет доступ к вашему не- volatile переменные. В старые времена, до C++11, люди использовали volatile, потому что это было все, что было, и вы могли заставить его работать с использованием явных функций барьера памяти, если бы вы знали что-то о том, как ваш компилятор выполняет оптимизацию (что было обычно без документов). Начиная с C++11 у нас есть std::atomic, и это единственный правильный способ справиться с разделением потоков между потоками, но, к сожалению, ассоциация с volatile сохраняется в устаревших документах и ​​умах старожилов. См. Почему volatile не считается полезным в многопоточном программировании на C или C++? подробнее.

Также актуально: Вводит ли ключевое слово C++ volatile ограничение памяти? (Нет, как вы обнаружили.)

Спасибо за ваше сообщение! Изучив квалификатор volatile, я пришел к выводу, что он служит трем основным целям: 1. Он гарантирует, что переменная volatile всегда будет считываться из памяти, а не из регистра. 2. Это гарантирует, что компилятор не будет оптимизировать код, связанный с переменной volatile. 3. Это гарантирует, что порядок инструкций, включающих переменную volatile и другую переменную volatile, не будет изменен. Я ничего не пропустил?

tang 27.06.2023 18:34

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