Понимание ключевого слова volatile в C++
Я пытаюсь понять, как ключевое слово volatile работает в C++.
Я посмотрел, какие виды оптимизации предотвращает volatile в C++?. Глядя на принятый ответ, похоже, что volatile отключает два вида оптимизации.
- Запрещает компиляторам кэшировать значение в регистре.
- Оптимизация доступа к этому значению, когда они кажутся ненужными с точки зрения вашей программы.
Я нашел аналогичную информацию на https://en.cppreference.com/w/cpp/language/as_if.
Доступ (чтение и запись) к изменчивым объектам происходит строго в соответствии с семантикой выражений, в которых они встречаются. В частности, они не переупорядочиваются по отношению к другим изменчивым доступам в том же потоке.
Я написал простую программу на C++, которая суммирует все значения в массиве для сравнения поведения простого int
s vs. volatile int
с. Обратите внимание, что частичные суммы не изменчивы.
Массив состоит из неквалифицированных int
с.
int foo(const std::array<int, 4>& input)
{
auto sum = 0xD;
for (auto element : input)
{
sum += element;
}
return sum;
}
Массив состоит из изменчивых int
s
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 на протяжении десятилетий разрабатывались для поддержки практики использования volatile
objects для создания мьютекса, который охраняет "обычные" объекты. В качестве простого примера: если поток 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]