我正在尝试理解C++中volatile关键字的工作原理。
我查看了What kinds of optimizations does 'volatile' prevent in C++?。从被接受的答案来看,volatile禁用了两种优化:
数组由未经过资格认证的
当我查看生成的汇编代码时,只有在普通的
使用clang9生成的代码。
我查看了What kinds of optimizations does 'volatile' prevent in C++?。从被接受的答案来看,volatile禁用了两种优化:
- 防止编译器将值缓存到寄存器中。
- 从程序的角度看,优化掉对该值的访问。
我编写了一个简单的C++程序,将数组中所有值相加,以比较普通的对volatile对象的访问(读写)严格遵循它们出现的表达式的语义。特别地,它们不会与同一线程上的其他volatile访问重新排序。
int
和volatile int
的行为。请注意,部分总和不是volatile的。数组由未经过资格认证的
int
组成。int foo(const std::array<int, 4>& input)
{
auto sum = 0xD;
for (auto element : input)
{
sum += element;
}
return sum;
}
数组由易失的 int
组成
int bar(const std::array<volatile int, 4>& input)
{
auto sum = 0xD;
for (auto element : input)
{
sum += element;
}
return sum;
}
当我查看生成的汇编代码时,只有在普通的
int
情况下才会使用SSE寄存器。据我所知,使用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
volatile
唯一的用例是在处理内存映射I/O和多进程共享内存时。 (我并不声称我的列表是详尽的,只是我遇到的两种情况。) - Eljay