繁体   English   中英

了解 c++ 中的 volatile 关键字

[英]Understanding volatile keyword in c++

我试图了解 volatile 关键字在 C++ 中的工作原理。

我看了一下C++ 中的“volatile”防止了哪些优化? . 查看接受的答案,看起来 volatile 禁用了两种优化

  1. 防止编译器将值缓存在寄存器中。
  2. 当您的程序的 POV 似乎不需要访问该值时,优化访问该值。

我在https://en.cppreference.com/w/cpp/language/as_if找到了类似的信息。

对 volatile 对象的访问(读取和写入)严格按照它们出现的表达式的语义进行。 特别是,它们不会针对同一线程上的其他易失性访问重新排序。

我编写了一个简单的 C++ 程序,它将数组中的所有值相加,以比较普通int s 与 volatile int s 的行为。 请注意,部分总和不是易变的。

数组由不合格的int组成。

int foo(const std::array<int, 4>& input)
{
    auto sum = 0xD;
    for (auto element : input)
    {
        sum += element;
    }
    return sum;
}

数组由 volatile int组成

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

C++ 中的volatile关键字是从 C 继承的,它的目的是作为一个通用的包罗万象的地方,以指示编译器应该允许读取或写入 ZA8CFDE6331BD59EB2AC96F8911C4B66 的可能性的地方它可能没有副作用。 由于不同平台可能引发的副作用种类会有所不同,因此标准留下了编译器编写者应该如何最好地为客户服务的判断的问题。

Microsoft 的 8088/8086 和更高版本的 x86 编译器几十年来一直被设计为支持使用volatile对象来构建保护“普通”对象的互斥锁的做法。 举个简单的例子:如果线程 1 执行以下操作:

ordinaryObject = 23;
volatileFlag = 1;
while(volatileFlag)
  doOtherStuffWhileWaiting();
useValue(ordinaryObject);

并且线程 2 定期执行以下操作:

if (volatileFlag)
{
  ordinaryObject++;
  volatileFlag=0;
}

那么对volatileFlag的访问将作为对 Microsoft 编译器的警告,即他们应该避免假设任何对象上的任何先前操作将如何与以后的操作交互。 其他语言(如 C#)中的volatile限定符也遵循此模式。

不幸的是,clang 和 gcc 都没有包含任何以这种方式处理volatile的选项,而是选择要求程序员使用编译器特定的内在函数来产生与 Microsoft 仅使用标准关键字volatile可以实现的相同语义,该语义旨在适用于这样的目的[根据标准的作者,“ volatile object 也是一个合适的 model 用于在多个进程之间共享的变量。”--参见Z80791B3AE7002CB88C24687002CB88C24687026D9FAA8F8/dgsc://www. www/C99RationaleV5.10.pdf页码。 76 ll。 25-26]

暂无
暂无

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

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