通过内联汇编锁定内存操作
我是低级别的新手,所以我完全忘记了你可能面临的问题,我甚至不确定我是否理解“原子”这个词.现在我试图通过扩展程序集围绕内存操作进行简单的原子锁.为什么?为了好奇.我知道我在这里重新发明轮子,可能会过度简化整个过程.
这个问题? >如果有效,为什么? 我只想做… >在内存操作之前,锁定. 代码: volatile int atomic_gate_memory = 0; static inline void atomic_open(volatile int *gate) { asm volatile ( "wait:n" "cmp %[lock],%[gate]n" "je waitn" "mov %[lock],%[gate]n" : [gate] "=m" (*gate) : [lock] "r" (1) ); } static inline void atomic_close(volatile int *gate) { asm volatile ( "mov %[lock],%[gate]n" : [gate] "=m" (*gate) : [lock] "r" (0) ); } 然后像: void *_malloc(size_t size) { atomic_open(&atomic_gate_memory); void *mem = malloc(size); atomic_close(&atomic_gate_memory); return mem; } #define malloc(size) _malloc(size) ..同样适用于calloc,realloc,free和fork(适用于linux). #ifdef _UNISTD_H int _fork() { pid_t pid; atomic_open(&atomic_gate_memory); pid = fork(); atomic_close(&atomic_gate_memory); return pid; } #define fork() _fork() #endif 为atomic_open加载堆栈帧后,objdump生成: 00000000004009a7 <wait>: 4009a7: 39 10 cmp %edx,(%rax) 4009a9: 74 fc je 4009a7 <wait> 4009ab: 89 10 mov %edx,(%rax) 另外,鉴于上面的拆卸;我可以假设我正在进行原子操作,因为它只是一条指令吗? 解决方法
在现代优化编译器中,寄存器是一个毫无意义的暗示. 我认为一个简单的自旋锁在x86上没有任何真正重大/明显的性能问题就是这样的.当然,真正的实现将在旋转一段时间之后使用系统调用(如Linux 在内联asm中实现尽可能多的这个,或者最好使用C11 stdatomic,就像这个semaphore implementation一样. ;;; UNTESTED ;;;;;;;; ;;; TODO: **IMPORTANT** fall back to OS-supported sleep/wakeup after spinning some ; first arg in rdi,in the AMD64 SysV ABI ;;;;;void spin_lock (volatile char *lock) global spin_unlock spin_unlock: ;; debug: check that the old value was non-zero. double-unlocking is a nasty bug mov byte [rdi],0 ret ;; The store has release semantics,but not sequential-consistency (which you'd get from an xchg or something),;; because acquire/release is enough to protect a critical section (hence the name) ;;;;;void spin_unlock(volatile char *lock) global spin_lock spin_lock: cmp byte [rdi],0 ; avoid writing to the cache line if we don't own the lock: should speed up the other thread unlocking jnz .spinloop mov al,1 ; only need to do this the first time,otherwise we know al is non-zero .retry: xchg al,[rdi] test al,al ; check if we actually got the lock jnz .spinloop ret ; no taken branches on the fast-path .spinloop: pause ; very old CPUs decode it as REP NOP,which is fine cmp byte [rdi],0 ; To get a compiler to do this in C++11,use a memory_order_acquire load jnz .spinloop jmp .retry 如果您使用原子标记的位域,则可以使用锁定bts(测试和设置)相当于xchg-with-1.你可以旋转bt或测试.要解锁,你需要锁btr,而不仅仅是btr,因为它将是字节的非原子读 – 修改 – 写,甚至包含32位. 使用字节或字大小的锁定,您甚至不需要锁定操作来解锁; release semantics are enough. glibc的 如果我们看到它已被锁定,这可以避免写入锁定.这避免了在运行拥有它的线程的核心的L1中使高速缓存行无效,因此它可以在解锁期间返回到“已修改”(MESIF或MOESI),具有较少的高速缓存一致性延迟. 我们也不会在循环中使用锁定操作来充斥CPU.我不确定这一般会减慢多少,但是所有等待相同螺旋锁的10个线程都会使内存仲裁硬件保持相当繁忙.这可能会减慢持有锁的线程或系统上其他无关线程的速度,同时它们会使用其他锁或内存. PAUSE也是必不可少的,以避免CPU对内存排序的错误推测.只有当您正在读取的内存被另一个内核修改时才退出循环.但是,我们不想在无争议的情况下暂停.在Skylake上,PAUSE等待的时间要长得多,比如~100cycles IIRC,所以你绝对应该将spinloop与初始检查分开进行解锁. 我确信英特尔和??AMD的优化手册都在讨论这个问题,请参阅x86标签维基以及大量其他链接. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |