首页 > 解决方案 > 了解 volatile asm 与 volatile 变量

问题描述

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

#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++ 8with下的编译g++ -Wall -Wextra -g -std=c++11 -O3 loop.cpp -o loop大致给出了以下时间:

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

编译器资源管理器为 提供以下count功能-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变量的限定会阻止编译器执行与 with 相同的循环asm volatile

标签: c++assemblyg++compiler-optimizationvolatile

解决方案


当你让i volatile你告诉编译器它不知道的东西可以改变它的值时。这意味着每次使用它时它都被迫加载它的值,并且每次写入它时它都必须存储它。什么时候i不是volatile编译器可以优化该同步。


推荐阅读