Ответ 1
Может ли компилятор кэшировать значение атомной переменной и развернуть цикл?
Компилятор не может кэшировать значение атомной переменной.
Однако, поскольку вы используете std::memory_order_relaxed
, это означает, что компилятор может свободно переупорядочивать нагрузки и хранить от/до этой атомной переменной в отношении других загрузок и хранилищ.
Также обратите внимание, что вызов функции, определение которой недоступно в этой единицы перевода, является барьером памяти компилятора. Это означает, что вызов не может быть переупорядочен по отношению к окружающим нагрузкам и магазинам и что все нелокальные переменные должны быть перезагружены из памяти после вызова, как если бы все они были отмечены как неустойчивые. (Локальные переменные, адрес которых не был передан в другом месте, не будут перезагружены, хотя).
Преобразование кода, которое вы хотели бы избежать, не было бы корректным преобразованием, поскольку это нарушило бы модель памяти С++: в первом случае у вас есть один заряд атомной переменной, за которым следует вызов do_the_job
, во втором, у вас есть несколько вызовов. Наблюдаемое поведение преобразованного кода может быть различным.
И заметка std:: memory_order:
Связь с изменчивым
В потоке выполнения доступ (чтение и запись) ко всем энергозависимым объектам гарантированно не будет переупорядочен относительно друг друга, но этот порядок не гарантируется наблюдаемым другим потоком, поскольку волатильный доступ не устанавливает inter -прозрачная синхронизация.
Кроме того, волатильные обращения не являются атомарными (параллельное чтение и запись - это гонка данных) и не заказывают память (доступ к энергонезависимой памяти может быть свободно переупорядочен вокруг нестабильного доступа).
Этот бит энергонезависимого доступа к памяти может быть свободно переупорядочен вокруг нестабильного доступа, это правда и для расслабленной атомистики, так как ослабленная загрузка и магазины могут быть переупорядочены в отношении других нагрузок и хранилищ.
Другими словами, украшение вашего атома с помощью volatile
не повлияет на поведение вашего кода.
Независимо от того, что атомные переменные С++ 11 не должны быть помечены ключевыми словами volatile
.
Вот пример того, как g++ - 5.2 отличает атомные переменные. Следующие функции:
__attribute__((noinline)) int f(std::atomic<int>& a) {
return a.load(std::memory_order_relaxed);
}
__attribute__((noinline)) int g(std::atomic<int>& a) {
static_cast<void>(a.load(std::memory_order_relaxed));
static_cast<void>(a.load(std::memory_order_relaxed));
static_cast<void>(a.load(std::memory_order_relaxed));
return a.load(std::memory_order_relaxed);
}
__attribute__((noinline)) int h(std::atomic<int>& a) {
while(a.load(std::memory_order_relaxed))
;
return 0;
}
Скомпилированный с помощью g++ -o- -Wall -Wextra -S -march=native -O3 -pthread -std=gnu++11 test.cc | c++filt > test.S
создает следующую сборку:
f(std::atomic<int>&):
movl (%rdi), %eax
ret
g(std::atomic<int>&):
movl (%rdi), %eax
movl (%rdi), %eax
movl (%rdi), %eax
movl (%rdi), %eax
ret
h(std::atomic<int>&):
.L4:
movl (%rdi), %eax
testl %eax, %eax
jne .L4
ret