我们考虑以下程序,这只是定时循环:
#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++ 8
和g++ -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 explorer为
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
限定条件阻止编译器执行与asm volatile
相同的循环? 最佳答案
当您制作i
volatile
时,您告诉编译器它不知道的内容可以更改其值。这意味着每次使用它时都必须加载它的值,并且每次写入它时都必须存储它。当i
不是volatile
时,编译器可以优化该同步。
关于c++ - 了解volatile asm与volatile变量,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50935690/