为什么可以针对非易失性访问重新排序此易失性访问?

big*_*iao 5 c++ volatile non-volatile

下面的代码示例来自一个介绍其效果的中文博客volatile.左边是C代码; 另一个是生成的汇编代码.

// cordering.c                                gcc -O2 -S -masm=intel cordering.c

int A;
volatile int B;
void foo()                                    mov   eax, DWORD PTR B[rip]
{                                             mov   DWORD PTR B[rip], 0
    A = B + 1;                                add   eax, 1
    B = 0;                                    mov   DWORD PTR A[rip], eax
}                                             ret
Run Code Online (Sandbox Code Playgroud)

正如我们在汇编代码中看到的那样,副作用A放在副作用之后B,即使B是volatile合格的.但是,cppreference.com说:

[W]在单个执行线程中,易失性访问不能优化或重新排序,具有在易失性访问之前排序或排序的另一个可见副作用.

这里的副作用A之前是排序的B,所以我认为编译器执行此操作是非法的.我对吗?


作为补充,博客说如果我们想要保证a volatile和non-volatiletype 之间的顺序,我们需要同时做到volatile:

// cordering.c                                gcc -O2 -S -masm=intel cordering.c

volatile int A;
volatile int B;
void foo()                                    mov   eax, DWORD PTR B[rip]
{                                             add   eax, 1
    A = B + 1;                                mov   DWORD PTR A[rip], eax
    B = 0;                                    mov   DWORD PTR B[rip], 0
}                                             ret
Run Code Online (Sandbox Code Playgroud)

Gor*_*gar 5

您链接的页面显示:

通过volatile限定类型的glvalue表达式进行的每次访问(读取或写入操作,成员函数调用等)都被视为可见的副作用,用于优化 (即,在单个执行线程内,volatile访问不能被优化或重新排序,具有在易失性访问之前排序或排序的另一个可见副作用.

因此,如果A不是volatile,则访问A不被视为可见的副作用,并且第二个语句不适用(因为它没有说明重新排序可见和不可见的访问).

编辑:请注意,cppreference不是C++的官方文档,而是社区的努力.我确实认为粗体陈述的意图是定义可见的副作用是什么,但是没有写清楚.

最权威的参考是C++标准(这里是得到它的一些选项).的N4659标准草案,定义了副作用在[intro.execution] ,第14段,和可见的副作用通过在一个定义2页链[intro.races] .我不是C++标准的专家,因此我无法在没有付出巨大努力的情况下破译标准所说的内容,但欢迎您尝试一下.

但是,有关允许编译器执行哪些优化的非正式解释,您可以查看cppreference 上的as-if规则.

编辑2:标准还正式指定作为-如果规则中[intro.execution] ,第7段:

对符合实现的最低要求是:
(7.1) - 通过volatile glvalues的访问严格根据抽象机器的规则进行评估.
(7.2) - 在程序终止时,写入文件的所有数据应与根据抽象语义产生的程序执行的可能结果之一相同.
(7.3) - 交互设备的输入和输出动态应以在程序等待输入之前提示输出实际传送的方式进行.构成交互设备的是实现定义的.

总之,任何优化是有效的,只要程序产生相同的输出,以及读取和写入volatile对象的正确顺序,它保存为原来的例子发生.

  • 试图翻译成简单的英语:"可见的副作用"只意味着它是修改该特定对象的最后一个副作用.如果尚未进行修改,则不可见.如果稍后对同一对象进行了修改,则先前的修改将不再可见. (2认同)
  • @GoranFlegar后者.它所说的*非常简短的故事是`volatile'并不意味着你可以避免跨线程同步,你仍然需要它来使副作用可见.我不知道如何更详细地将其翻译成普通英语. (2认同)