C++中忽略了易失性说明符

Pas*_* By 1 c++ volatile c++11

我是C++的新手,最近我遇到了一些关于变量意味着什么的信息volatile.据我所知,这意味着对变量的读取或写入永远不会被优化.

然而,当我声明一个volatile不是1,2,4,8字节大的变量时会出现一种奇怪的情况:编译器(启用了C++ 11的gnu)似乎忽略了volatile说明符

#define expand1 a, a, a, a, a, a, a, a, a, a
#define expand2 // ten expand1 here, expand3 to expand5 follows
// expand5 is the equivalent of 1e+005 a, a, ....

struct threeBytes { char x, y, z; };
struct fourBytes { char w, x, y, z; };

int main()
{
   // requires ~1.5sec
   foo<int>();

   // doesn't take time
   foo<threeBytes>();

   // requires ~1.5sec
   foo<fourBytes>();
}

template<typename T>
void foo()
{
   volatile T a;

   // With my setup, the loop does take time and isn't optimized out
   clock_t start = clock();
   for(int i = 0; i < 100000; i++);
   clock_t end = clock();
   int interval = end - start;

   start = clock();
   for(int i = 0; i < 100000; i++) expand5;
   end = clock();

   cout << end - start - interval << endl;
}
Run Code Online (Sandbox Code Playgroud)

他们的时间是

  • foo<int>():~1.5s
  • foo<threeBytes>():0

我用不同的变量(用户定义与否)测试了1到8个字节,只有1,2,4,8需要时间来运行.这是一个只存在于我的设置中的错误,还是volatile对编译器的请求,而不是绝对的东西?

PS四字节版本总是占用其他时间的一半,也是混乱的来源

vso*_*tco 5

结构版本可能会被优化,因为编译器意识到没有副作用(没有读取或写入变量a),无论如何volatile.你基本上有一个无操作,a;所以编译器可以做任何你喜欢的事情; 它不是被迫展开循环或优化它,所以这volatile并不重要.在ints 的情况下,似乎没有优化,但这与以下用例一致volatile:只有在循环中有可能"访问对象"(即读取或写入)时,应该期望非优化.然而,构成"访问对象"的内容是实现定义的(尽管大部分时间它遵循常识),请参见底部的编辑3.

这里的玩具示例:

#include <iostream>
#include <chrono>

int main()
{
    volatile int a = 0;

    const std::size_t N = 100000000;

    // side effects, never optimized
    auto start = std::chrono::steady_clock::now();
    for (std::size_t i = 0 ; i < N; ++i)
        ++a; // side effect (write)
    auto end = std::chrono::steady_clock::now();
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
              <<  " ms" << std::endl;

    // no side effects, may or may not be optimized out
    start = std::chrono::steady_clock::now();
    for (std::size_t i = 0 ; i < N; ++i)
        a; // no side effect, this is a no-op
    end = std::chrono::steady_clock::now();
    std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()
              <<  " ms" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

编辑

对于标量类型,no-op实际上并未实现优化,正如您在此最小示例中所看到的那样.对于struct的,虽然,它被优化掉了.在我链接的示例中,clang没有优化代码而没有优化,但优化了两个循环-O3.gcc没有优化就不会优化循环,但只优化第一个循环并进行优化.

编辑2

clang吐出警告:warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue].所以我最初的猜测是正确的,编译器可以优化no-ops,但它不是强制的.为什么它为structs而不是标量类型是我不理解的东西,但它是编译器的选择,并且它是标准兼容的.由于某种原因,只有在no-op为a时它才会发出此警告struct,并且当它是标量类型时不会发出警告.

还要注意,你没有"读/写",你只有一个无操作,所以你不应该期待任何东西volatile.

编辑3

来自金书(C++标准)

7.1.6.1/8 cv-qualifiers [dcl.type.cv]

什么构成对具有volatile限定类型的对象的访问是实现定义的....

因此,编译器需要决定何时优化循环.在大多数情况下,它遵循常识:读取或写入对象时.