我们考虑以下程序,这只是定时循环:

#include <cstdlib>

std::size_t count(std::size_t n)
{
#ifdef VOLATILEVAR
    volatile std::size_t i = 0;
#else
    std::size_t i = 0;
#endif
    while (i < n) {
#ifdef VOLATILEASM
        asm volatile("": : :"memory");
#endif
        ++i;
    }
    return i;
}

int main(int argc, char* argv[])
{
    return count(argc > 1 ? std::atoll(argv[1]) : 1);
}

为了便于阅读,具有volatile变量和volatile asm的版本如下:
#include <cstdlib>

std::size_t count(std::size_t n)
{
    volatile std::size_t i = 0;
    while (i < n) {
        asm volatile("": : :"memory");
        ++i;
    }
    return i;
}

int main(int argc, char* argv[])
{
    return count(argc > 1 ? std::atoll(argv[1]) : 1);
}

g++ 8g++ -Wall -Wextra -g -std=c++11 -O3 loop.cpp -o loop下进行编译的时间大致如下:
  • default: 0m0.001s
  • -DVOLATILEASM: 0m1.171s
  • -DVOLATILEVAR: 0m5.954s
  • -DVOLATILEVAR -DVOLATILEASM: 0m5.965s

  • 我的问题是:为什么?默认版本是正常的,因为编译器已对循环进行了优化。但是我很难理解为什么-DVOLATILEVAR-DVOLATILEASM长得多,因为两者都应强制循环运行。

    Compiler explorercount提供以下-DVOLATILEASM函数:
    count(unsigned long):
      mov rax, rdi
      test rdi, rdi
      je .L2
      xor edx, edx
    .L3:
      add rdx, 1
      cmp rax, rdx
      jne .L3
    .L2:
      ret
    

    以及-DVOLATILEVAR(和组合的-DVOLATILEASM -DVOLATILEVAR):
    count(unsigned long):
      mov QWORD PTR [rsp-8], 0
      mov rax, QWORD PTR [rsp-8]
      cmp rdi, rax
      jbe .L2
    .L3:
      mov rax, QWORD PTR [rsp-8]
      add rax, 1
      mov QWORD PTR [rsp-8], rax
      mov rax, QWORD PTR [rsp-8]
      cmp rax, rdi
      jb .L3
    .L2:
      mov rax, QWORD PTR [rsp-8]
      ret
    

    为什么会这样呢?为什么变量的volatile限定条件阻止编译器执行与asm volatile相同的循环?

    最佳答案

    当您制作i volatile时,您告诉编译器它不知道的内容可以更改其值。这意味着每次使用它时都必须加载它的值,并且每次写入它时都必须存储它。当i不是volatile时,编译器可以优化该同步。

    关于c++ - 了解volatile asm与volatile变量,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50935690/

    10-10 09:50