Понимание ключевого слова volatile в C++

Я пытаюсь понять, как ключевое слово volatile работает в C++.

Я посмотрел, какие виды оптимизации предотвращает volatile в C++?. Глядя на принятый ответ, похоже, что volatile отключает два вида оптимизации.

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

Я нашел аналогичную информацию на https://en.cppreference.com/w/cpp/language/as_if.

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

Я написал простую программу на C++, которая суммирует все значения в массиве для сравнения поведения простого ints vs. volatile intс. Обратите внимание, что частичные суммы не изменчивы.

Массив состоит из неквалифицированных intс.

int foo(const std::array<int, 4>& input)
{
    auto sum = 0xD;
    for (auto element : input)
    {
        sum += element;
    }
    return sum;
}

Массив состоит из изменчивых ints

int bar(const std::array<volatile int, 4>& input)
{
    auto sum = 0xD;
    for (auto element : input)
    {
        sum += element;
    }
    return sum;
}

Когда я смотрю на сгенерированный код сборки, регистры SSE используются только в случае простого intс. Насколько я понимаю, код, использующий регистры SSE, не оптимизирует чтение и не меняет их порядок друг с другом. Цикл развернут, поэтому веток тоже нет. Единственная причина, по которой я могу объяснить, почему генерация кода отличается, заключается в следующем: можно ли переупорядочить изменчивые чтения до того, как произойдет накопление? Ясно,sumне летучий. Если такое переупорядочивание - плохо, есть ли ситуация / пример, которые могут проиллюстрировать проблему?

Код, созданный с использованием clang9

foo(std::array<int, 4ul> const&):                # @foo(std::array<int, 4ul> const&)
        movdqu  (%rdi), %xmm0
        pshufd  $78, %xmm0, %xmm1       # xmm1 = xmm0[2,3,0,1]
        paddd   %xmm0, %xmm1
        pshufd  $229, %xmm1, %xmm0      # xmm0 = xmm1[1,1,2,3]
        paddd   %xmm1, %xmm0
        movd    %xmm0, %eax
        addl    $13, %eax
        retq
bar(std::array<int volatile, 4ul> const&):               # @bar(std::array<int volatile, 4ul> const&)
        movl    (%rdi), %eax
        addl    4(%rdi), %eax
        addl    8(%rdi), %eax
        movl    12(%rdi), %ecx
        leal    (%rcx,%rax), %eax
        addl    $13, %eax
        retq

1 ответ

Решение

В volatileКлючевое слово в C++ было унаследовано от C, где оно было задумано как универсальное средство, чтобы указать места, где компилятор должен учитывать возможность того, что чтение или запись объекта может иметь побочные эффекты, о которых он не знает. Поскольку виды побочных эффектов, которые могут быть вызваны, будут различаться для разных платформ, Стандарт оставляет вопрос о том, какие допущения сделать, на усмотрение авторов компиляторов относительно того, как они должны наилучшим образом обслуживать своих клиентов.

Компиляторы Microsoft для 8088/8086 и более поздних версий x86 на протяжении десятилетий разрабатывались для поддержки практики использования volatileobjects для создания мьютекса, который охраняет "обычные" объекты. В качестве простого примера: если поток 1 делает что-то вроде:

ordinaryObject = 23;
volatileFlag = 1;
while(volatileFlag)
  doOtherStuffWhileWaiting();
useValue(ordinaryObject);

и поток 2 периодически делает что-то вроде:

if (volatileFlag)
{
  ordinaryObject++;
  volatileFlag=0;
}

затем доступ к volatileFlagпослужит предупреждением для компиляторов Microsoft, что им следует воздерживаться от предположений о том, как любые предыдущие действия над любыми объектами будут взаимодействовать с последующими действиями. Этому шаблону следовалиvolatile квалификаторы на других языках, таких как C#.

К сожалению, ни clang, ни gcc не содержат никаких опций для лечения volatile таким образом, предпочитая вместо этого требовать, чтобы программисты использовали специфичные для компилятора встроенные функции, чтобы получить ту же семантику, которую Microsoft могла бы достичь, используя только ключевое слово Standard volatile который должен был подходить для таких целей [по мнению авторов Стандарта, "A volatileобъект также является подходящей моделью для переменной, совместно используемой несколькими процессами "- см. http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf стр. 76 ll. 25-26]

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