简体   繁体   English

为什么可以相对于非易失性访问对这种易失性访问进行重新排序?

[英]Why can this volatile access be reordered with respect to a non-volatile access?

The below code example is from a Chinese blog which introduces the effect of volatile . 下面的代码示例来自中文博客 ,该博客介绍了volatile的作用。 The left side is the C code; 左侧是C代码; the other is the generated assembly code. 另一个是生成的汇编代码。

// 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

As we can see in the assembly code, the side-effect of A is placed after the side-effect of B , even though B is volatile qualified. 正如我们在汇编代码中看到的那样,即使Bvolatile限定的, A的副作用也要放在B的副作用之后。 However, cppreference.com says : 但是, cppreference.com说

[W]ithin a single thread of execution, volatile accesses cannot be optimized out or reordered with another visible side effect that is sequenced-before or sequenced-after the volatile access . 在单个执行线程中,无法优化易失性访问,也不会因易失性访问之前或之后的另一个可见副作用而被优化或重新排序

Here the side-effect of A is sequenced before B , so I think it's illegal for the compiler to do this. 这里A的副作用在B之前排序,所以我认为编译器这样做是非法的。 Am I right? 我对吗?


As a supplement, the blog said if we want to guarantee the sequence between a volatile and non-volatile type, we need to make both volatile : 作为补充,该博客说,如果我们要保证volatile non-volatile类型和non-volatile类型之间的顺序,我们需要使两者都具有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

The page you linked says: 您链接的页面显示:

Every access (read or write operation, member function call, etc.) made through a glvalue expression of volatile-qualified type is treated as a visible side-effect for the purposes of optimization (that is, within a single thread of execution, volatile accesses cannot be optimized out or reordered with another visible side effect that is sequenced-before or sequenced-after the volatile access. 出于优化目的 (即在单个执行线程中, 通过volatile限定类型的glvalue表达式进行的每次访问(读或写操作,成员函数调用等)都被视为可见的副作用)无法对访问进行优化,也不会因其他易见的副作用(在易失性访问之前或之后进行排序)而重新排序。

Thus, if A is not volatile , accesses to A are not treated as visible side-effects, and the second statement doesn't apply (as it doesn't say anything about reordering visible and non-visible accesses). 因此,如果A不是volatile ,则对A访问不会被视为可见的副作用,并且第二条语句不适用(因为它没有说明对可见和不可见访问进行重新排序)。

EDIT: Note that cppreference is not official documentation for C++, but a community effort. 编辑:请注意,cppreference不是C ++的官方文档,而是社区的工作。 I do believe that the intent of the statement in bold was to define what a visible side-effect is, but it is not written clearly. 我确实认为,黑体字的意思是要定义什么是可见的副作用 ,但是并没有清楚地写出来。

The definitive reference is the C++ standard ( here are some options for getting it). 权威参考是C ++标准( 这里有一些选择)。 The N4659 standard draft, defines side-effects in [intro.execution] , paragraph 14, and visible side-effects through a 2-page chain of definitions in [intro.races] . N4659标准草案在[intro.execution]第14段中定义了副作用 ,并通过[intro.races]中两页的定义链定义了可见的副作用 I am not an expert on the C++ standard, so I cannot decipher what exactly the standard says without significant effort, but you are welcome to give it a try. 我不是C ++标准的专家,因此如果不付出很大的努力,我无法完全理解该标准的内容,但是欢迎您尝试一下。

However, for an informal explanation of what optimizations the compiler is allowed to do, you can take a look at the as-if rule on cppreference. 但是,对于允许编译器进行哪些优化的非正式解释,您可以看一下cppreference的as-if规则

EDIT 2: the standard also formally specifies the as-if rule in [intro.execution] , paragraph 7: 编辑2:该标准还在[intro.execution]第7段中正式指定了按条件规则

The least requirements on a conforming implementation are: 符合标准的实现的最低要求是:
(7.1) — Accesses through volatile glvalues are evaluated strictly according to the rules of the abstract machine. (7.1)-严格根据抽象机的规则评估通过易失性glvalue进行的访问。
(7.2) — At program termination, all data written into files shall be identical to one of the possible results that execution of the program according to the abstract semantics would have produced. (7.2)—在程序终止时,写入文件的所有数据应与根据抽象语义执行程序可能产生的结果之一相同。
(7.3) — The input and output dynamics of interactive devices shall take place in such a fashion that prompting output is actually delivered before a program waits for input. (7.3)—交互设备的输入和输出动态必须以一种方式进行,即在程序等待输入之前,实际上会发出提示输出。 What constitutes an interactive device is implementation-defined. 构成交互式设备的是实现定义的。

In short, any optimization is valid, as long as the program produces the same output, and the reads and writes to volatile objects happen in the correct order, which holds for your original example. 简而言之,只要程序产生相同的输出,并且对易失性对象的读取和写入均以正确的顺序进行,那么任何优化都是有效的,这适用于您的原始示例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM