标准 C++11 是否保证 `volatile atomic<T>` 具有两种语义(volatile + atomic)?
Does standard C++11 guarantee that `volatile atomic<T>` has both semantics (volatile + atomic)?
众所周知,std::atomic
和 volatile
是不同的东西。
有两个主要区别:
两个优化可以针对std::atomic<int> a;
,但不能针对volatile int a;
:
- 融合操作:
a = 1; a = 2;
可以在 a = 2;
上由编译器替换
- 常量传播:
a = 1; local = a;
可以在a = 1; local = 1;
上由编译器替换
跨 atomic/volatile 操作重新排序普通 reads/writes:
- for
volatile int a;
any volatile-read/write-operations 无法重新排序。但是附近的普通 reads/writes 仍然可以在 volatile reads/writes. 周围重新排序
- 用于
std::atomic a;
附近普通 reads/writes 的重新排序受限于用于原子操作的内存屏障 a.load(std::memory_order_...);
即volatile
不引入内存栅栏,但是std::atomic
可以做到。
正如文章中描述的那样:
- Herb Sutter,2009 年 1 月 8 日 - 第 1 部分:http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484
- Herb Sutter,2009 年 1 月 8 日 - 第 2 部分:http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484?pgno=2
例如std::atomic
应该用于并发多线程程序(CPU-Core <-> CPU-Core),但是volatile
应该是用于访问设备上的 Mamory 映射区域(CPU-核心 <-> 设备)。
但如果需要,两者都具有不寻常的语义,并且具有无锁编码所需的任何或所有原子性 and/or 顺序保证,即如果需要 volatile std::atomic<>
,则出于以下几个原因需要:
- ordering:防止普通 reads/writes 的重新排序,例如,从 CPU-RAM 读取,数据是使用设备 DMA 控制器
例如:
char cpu_ram_data_written_by_device[1024];
device_dma_will_write_here( cpu_ram_data_written_by_device );
// physically mapped to device register
volatile bool *device_ready = get_pointer_device_ready_flag();
//... somewhere much later
while(!device_ready); // spin-lock (here should be memory fence!!!)
for(auto &i : cpu_ram_data_written_by_device) std::cout << i;
- 溢出:CPU 写入 CPU-RAM,然后设备 DMA 控制器从该内存读取:https://en.wikipedia.org/wiki/Register_allocation#Spilling
示例:
char cpu_ram_data_will_read_by_device[1024];
device_dma_will_read_it( cpu_ram_data_written_by_device );
// physically mapped to device register
volatile bool *data_ready = get_pointer_data_ready_flag();
//... somewhere much later
for(auto &i : cpu_ram_data_will_read_by_device) i = 10;
data_ready=true; //spilling cpu_ram_data_will_read_by_device to RAM, should be memory fence
- atomic:保证 volatile 操作是原子的 - 即它将由单个操作而不是多个操作组成 - 即一个 8 字节操作而不是两个 4 -字节操作
为此,Herb Sutter 在 2009 年 1 月 8 日关于 volatile atomic<T>
说:http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484?pgno=2
Finally, to express a variable that both has unusual semantics and has
any or all of the atomicity and/or ordering guarantees needed for
lock-free coding, only the ISO C++0x draft Standard provides a direct
way to spell it: volatile atomic.
但是 modern 标准 C++11(不是 C++0x 草案)、C++14 和 C++17 保证 volatile atomic<T>
有两种语义(volatile + atomic)?
volatile atomic<T>
是否保证来自 volatile 和 atomic 的最严格保证?
- 如
volatile
:避免问题开头所述的融合操作和常数传播
- 与
std::atomic
一样:引入内存栅栏以提供排序、溢出和原子性。
我们可以 reinterpret_cast
从 volatile int *ptr;
到 volatile std::atomic<int>*
吗?
是的,确实如此。
第 29.6.5 节,"Requirements for operations on atomic types"
Many operations are volatile-qualified. The “volatile as device register” semantics have not changed in the standard. This qualification means that volatility is preserved when applying these operations to volatile objects.
我检查了 2008 年到 2016 年的工作草稿,所有草稿都是相同的文本。因此它应该适用于C++11、C++14和C++17。
And can we do reinterpret_cast from volatile int *ptr;
to volatile
std::atomic<int>*
?
当且仅当 ABI 表明两种类型(此处 int
和 std::atomic<int>
)具有相同的表示和限制:相同的大小、对齐方式和可能的位模式;相同位模式的相同含义。
一切可变的都与 ABI 直接相关:可变限定的变量必须在序列点具有规范的 ABI 表示,并且对可变对象的操作仅假定它们遵循其 ABI 要求,仅此而已。因此,无论何时在 C 或 C++ 中使用 volatile,您都可以选择依赖语言标准或平台 ABI。
(我希望这个答案不会被删除,因为有些人鄙视 volatile 语义并且依赖于 ABI 和平台特定的概念。)
众所周知,std::atomic
和 volatile
是不同的东西。
有两个主要区别:
两个优化可以针对
std::atomic<int> a;
,但不能针对volatile int a;
:- 融合操作:
a = 1; a = 2;
可以在a = 2;
上由编译器替换
- 常量传播:
a = 1; local = a;
可以在a = 1; local = 1;
上由编译器替换
- 融合操作:
跨 atomic/volatile 操作重新排序普通 reads/writes:
- for
volatile int a;
any volatile-read/write-operations 无法重新排序。但是附近的普通 reads/writes 仍然可以在 volatile reads/writes. 周围重新排序
- 用于
std::atomic a;
附近普通 reads/writes 的重新排序受限于用于原子操作的内存屏障a.load(std::memory_order_...);
- for
即volatile
不引入内存栅栏,但是std::atomic
可以做到。
正如文章中描述的那样:
- Herb Sutter,2009 年 1 月 8 日 - 第 1 部分:http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484
- Herb Sutter,2009 年 1 月 8 日 - 第 2 部分:http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484?pgno=2
例如std::atomic
应该用于并发多线程程序(CPU-Core <-> CPU-Core),但是volatile
应该是用于访问设备上的 Mamory 映射区域(CPU-核心 <-> 设备)。
但如果需要,两者都具有不寻常的语义,并且具有无锁编码所需的任何或所有原子性 and/or 顺序保证,即如果需要 volatile std::atomic<>
,则出于以下几个原因需要:
- ordering:防止普通 reads/writes 的重新排序,例如,从 CPU-RAM 读取,数据是使用设备 DMA 控制器
例如:
char cpu_ram_data_written_by_device[1024];
device_dma_will_write_here( cpu_ram_data_written_by_device );
// physically mapped to device register
volatile bool *device_ready = get_pointer_device_ready_flag();
//... somewhere much later
while(!device_ready); // spin-lock (here should be memory fence!!!)
for(auto &i : cpu_ram_data_written_by_device) std::cout << i;
- 溢出:CPU 写入 CPU-RAM,然后设备 DMA 控制器从该内存读取:https://en.wikipedia.org/wiki/Register_allocation#Spilling
示例:
char cpu_ram_data_will_read_by_device[1024];
device_dma_will_read_it( cpu_ram_data_written_by_device );
// physically mapped to device register
volatile bool *data_ready = get_pointer_data_ready_flag();
//... somewhere much later
for(auto &i : cpu_ram_data_will_read_by_device) i = 10;
data_ready=true; //spilling cpu_ram_data_will_read_by_device to RAM, should be memory fence
- atomic:保证 volatile 操作是原子的 - 即它将由单个操作而不是多个操作组成 - 即一个 8 字节操作而不是两个 4 -字节操作
为此,Herb Sutter 在 2009 年 1 月 8 日关于 volatile atomic<T>
说:http://www.drdobbs.com/parallel/volatile-vs-volatile/212701484?pgno=2
Finally, to express a variable that both has unusual semantics and has any or all of the atomicity and/or ordering guarantees needed for lock-free coding, only the ISO C++0x draft Standard provides a direct way to spell it: volatile atomic.
但是 modern 标准 C++11(不是 C++0x 草案)、C++14 和 C++17 保证 volatile atomic<T>
有两种语义(volatile + atomic)?
volatile atomic<T>
是否保证来自 volatile 和 atomic 的最严格保证?
- 如
volatile
:避免问题开头所述的融合操作和常数传播 - 与
std::atomic
一样:引入内存栅栏以提供排序、溢出和原子性。
我们可以 reinterpret_cast
从 volatile int *ptr;
到 volatile std::atomic<int>*
吗?
是的,确实如此。
第 29.6.5 节,"Requirements for operations on atomic types"
Many operations are volatile-qualified. The “volatile as device register” semantics have not changed in the standard. This qualification means that volatility is preserved when applying these operations to volatile objects.
我检查了 2008 年到 2016 年的工作草稿,所有草稿都是相同的文本。因此它应该适用于C++11、C++14和C++17。
And can we do reinterpret_cast from
volatile int *ptr;
tovolatile std::atomic<int>*
?
当且仅当 ABI 表明两种类型(此处 int
和 std::atomic<int>
)具有相同的表示和限制:相同的大小、对齐方式和可能的位模式;相同位模式的相同含义。
一切可变的都与 ABI 直接相关:可变限定的变量必须在序列点具有规范的 ABI 表示,并且对可变对象的操作仅假定它们遵循其 ABI 要求,仅此而已。因此,无论何时在 C 或 C++ 中使用 volatile,您都可以选择依赖语言标准或平台 ABI。
(我希望这个答案不会被删除,因为有些人鄙视 volatile 语义并且依赖于 ABI 和平台特定的概念。)