kay*_*kay 14 c x86 gcc synchronization
请考虑以下压缩代码:
/* Compile: gcc -pthread -m32 -ansi x.c */
#include <stdio.h>
#include <inttypes.h>
#include <pthread.h>
static volatile uint64_t v = 0;
void *func (void *x) {
__sync_add_and_fetch (&v, 1);
return x;
}
int main (void) {
pthread_t t;
pthread_create (&t, NULL, func, NULL);
pthread_join (t, NULL);
printf ("v = %"PRIu64"\n", v);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我有一个uint64_t我想以原子方式递增的变量,因为该变量是多线程程序中的计数器.为了实现原子性,我使用了GCC的原子内置.
如果我编译amd64系统(-m64),生成的汇编代码很容易理解.通过使用a lock addq,处理器保证增量是原子的.
400660: f0 48 83 05 d7 09 20 lock addq $0x1,0x2009d7(%rip)
Run Code Online (Sandbox Code Playgroud)
但是相同的C代码在ia32系统(-m32)上产生了非常复杂的ASM代码:
804855a: a1 28 a0 04 08 mov 0x804a028,%eax
804855f: 8b 15 2c a0 04 08 mov 0x804a02c,%edx
8048565: 89 c1 mov %eax,%ecx
8048567: 89 d3 mov %edx,%ebx
8048569: 83 c1 01 add $0x1,%ecx
804856c: 83 d3 00 adc $0x0,%ebx
804856f: 89 ce mov %ecx,%esi
8048571: 89 d9 mov %ebx,%ecx
8048573: 89 f3 mov %esi,%ebx
8048575: f0 0f c7 0d 28 a0 04 lock cmpxchg8b 0x804a028
804857c: 08
804857d: 75 e6 jne 8048565 <func+0x15>
Run Code Online (Sandbox Code Playgroud)
这是我不明白的:
lock cmpxchg8b 并保证,如果预期值仍驻留在目标地址改变的变量只写.比较和交换保证以原子方式发生.可能是否有"脏读"并不重要,但有人可以请一个简短的证据表明没有问题吗?
进一步:为什么生成的代码跳回0x8048565而不是0x804855a?我很肯定,如果其他作家也只增加变量,这只是正确的.这是__sync_add_and_fetch函数的牵连要求吗?
Nec*_*lis 17
由于它正确对齐(并且它适合一个缓存线),并且由于英特尔以这种方式制定了规范,因此保证读取是原子的,请参阅英特尔架构手册第1,4,4.1节:
跨越4字节边界的字或双字操作数或跨越8字节边界的四字操作数被认为是未对齐的,并且需要两个单独的存储器总线周期来进行访问.
第3A卷8.1.1:
奔腾处理器(以及更新的处理器)保证以下额外的内存操作将始终以原子方式执行:
•读取或写入在64位边界上对齐的四字
•16位访问非缓存存储器位置,适合32位数据总线
P6系列处理器(以及之后的新处理器)保证以下额外的内存操作将始终以原子方式执行:
•未对齐的16位,32位和64位访问缓存内存,适合缓存行
因此,通过对齐,它可以在1个周期内读取,并且它适合于一个高速缓存行,从而使读取原子化.
代码跳回,mov因为指针已经加载,不需要再次加载它们,如果失败cmpxchg8b则设置lock为目标中的值:
lock英特尔ISA手册Vol.2A:
比较EDX:EAX和m64.如果相等,则设置ZF并将ECX:EBX加载到m64.否则,清除ZF并将m64加载到EDX:EAX中.
因此,代码只需要递增新返回的值并再次尝试.如果我们在C代码中使用它变得更容易:
value = dest; // non-atomic but usually won't tear
while(!CAS8B(&dest,value,value + 1))
{
value = dest; // atomic; part of lock cmpxchg8b
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11899 次 |
| 最近记录: |