我是否需要仅在锁定时访问的变量的volatile限定符?在此代码中,可以从n
中删除volatile限定符,可能会在concurrent_foo
同时执行时更改行为。
#ifndef __GNUC__
#error __sync_lock builtins are only available with GCC
#endif
volatile int n = 0;
static volatile int lock = 0;
void concurrent_foo () {
while (__sync_lock_test_and_set (&lock, 1));
// Non-atomic operation, protected by spinlock above.
int x = n % 2 + 1;
n = n + x;
__sync_lock_release (&lock);
}
我知道volatile限定符指示编译器不要优化对变量的内存访问。我也明白__sync_lock内置问题是一个(完整?)内存屏障,内存访问不应该交叉。但是,在此示例代码中,获取n
,将其缓存在寄存器中,计算新值,然后将其写回n
将是安全的。
使用-O3
使用GCC到i686源代码进行编译,发现有两次内存提取,无论如何:
concurrent_foo:
movl $1, %edx
.L2:
movl %edx, %eax
xchgl lock, %eax
testl %eax, %eax
jne .L2
movl n, %eax
movl n, %edx
movl %eax, %ecx
shrl $31, %ecx
addl %ecx, %eax
andl $1, %eax
subl %ecx, %eax
leal 1(%edx,%eax), %eax
movl %eax, n
movl $0, lock
ret
如果没有volatile限定符,我会得到微妙的不同代码,其中n
只被提取一次:
concurrent_foo:
movl $1, %edx
.L2:
movl %edx, %eax
xchgl lock, %eax
testl %eax, %eax
jne .L2
movl n, %edx
movl %edx, %ecx
shrl $31, %ecx
leal (%edx,%ecx), %eax
andl $1, %eax
subl %ecx, %eax
leal 1(%edx,%eax), %eax
movl %eax, n
movl $0, lock
ret
在这两种情况下,在保持锁定时会发生对n
的存储器访问,因此应该是“正确的”。但是,我不确定我是否真的得到了保证。 volatile限定符阻止了我想要的性能优化,并且不会影响操作的结果(任何时候n
都不会是偶数)。