libDispatch 省去了队列锁,代替的是大量的原子操作:
文件位置:libdispatch/src/hw_shims.h
#if 0 && defined(__i386__) || defined(__x86_64__)
#define dispatch_atomic_xchg(p, n) ({ typeof(*(p)) _r; asm("xchg %0, %1" : "=r" (_r) : "m" (*(p)), "0" (n)); _r; })
#else
#define dispatch_atomic_xchg(p, n) ((typeof(*(p)))__sync_lock_test_and_set((p), (n)))
#endif
#define dispatch_atomic_cmpxchg(p, o, n) __sync_bool_compare_and_swap((p), (o), (n))
#define dispatch_atomic_inc(p) __sync_add_and_fetch((p), 1)
#define dispatch_atomic_dec(p) __sync_sub_and_fetch((p), 1)
#define dispatch_atomic_add(p, v) __sync_add_and_fetch((p), (v))
#define dispatch_atomic_sub(p, v) __sync_sub_and_fetch((p), (v))
#define dispatch_atomic_or(p, v) __sync_fetch_and_or((p), (v))
#define dispatch_atomic_and(p, v) __sync_fetch_and_and((p), (v))
#if defined(__i386__) || defined(__x86_64__)
/* GCC emits nothing for __sync_synchronize() on i386/x86_64. */
#define dispatch_atomic_barrier() __asm__ __volatile__("mfence")
#else
#define dispatch_atomic_barrier() __sync_synchronize()
#endif
#else
#error "Please upgrade to GCC 4.2 or newer."
#endif
#if defined(__i386__) || defined(__x86_64__)
#define _dispatch_hardware_pause() asm("pause")
#define _dispatch_debugger() asm("int3")
#else
#define _dispatch_hardware_pause() asm("")
#define _dispatch_debugger() asm("trap")
#endif
// really just a low level abort()
#define _dispatch_hardware_crash() __builtin_trap()
1. 原子操作
首先是dispatch_atomic_*相关的操作,加减与或等;这写方法依赖于GCC的相关原子操作:
type __sync_fetch_and_add (type *ptr, type value);
type __sync_fetch_and_sub (type *ptr, type value);
type __sync_fetch_and_or (type *ptr, type value);
type __sync_fetch_and_and (type *ptr, type value);
type __sync_fetch_and_xor (type *ptr, type value);
type __sync_fetch_and_nand (type *ptr, type value);
type __sync_add_and_fetch (type *ptr, type value);
type __sync_sub_and_fetch (type *ptr, type value);
type __sync_or_and_fetch (type *ptr, type value);
type __sync_and_and_fetch (type *ptr, type value);
type __sync_xor_and_fetch (type *ptr, type value);
type __sync_nand_and_fetch (type *ptr, type value);
这些Gcc内置函数可以实现对int,long,long long基本类型的 + / - / 前置++ / 后置++ / & / ^ / | / -- 等基本操作,比如操作一个全局计数器递增,就可以用:
__sync_fetch_and_add (&globel,1);
实现原子操作,其内部不是用pthread等锁来实现的,而是直接用了lock的汇编指令来实现,汇编了一下上面的代码:
leal 40(%esp), %eax
lock addl $1, (%eax)
.loc 3 353 0
movl 40(%esp), %edx
movl $.LC7, %eax
可以看出,这是 靠底层CPU指令来支持的,有资料现实,同样递增计数器,使用这种内置的支持比pthread_mutex的锁支持要快5~6倍。
这里需要留意一下
__sync_bool_compare_and_swap((p), (o), (n))
这是Lock Free给予CAS的一种机制,原理就是(a) 如果p==o,那么将p设置为n, 然后返回true; (b), 如果(a)不成立,那么不做任何处理返回false:
原理的抽象代码如下:
bool CAS(inptr_t* addr, intptr_t oldv, intptr_t newv)
atomically{
if(*addr == oldv)
{
*addr = newv;
return true;
}
else
{
return false
}
}
当然真正的实现代码不是这样的,其核心是利用处理器的一些特殊的原子指令来避免传统并行设计中对锁(lock)的使用。
Linux上一种典型的实现是:
inline int CAS(unsigned long *mem,unsigned long newval,unsigned long oldval)
{
__typeof (*mem) ret;
asm __volatile ("lock; cmpxchgl %2, %1"
: "=a" (ret), "=m" (*mem)
: "r" (newval), "m" (*mem), "0" (oldval));
return (int) ret;
}
有人对性能做了一个对比,可以参看:Lock-Free的栈实现及与加锁实现的性能对比
2. barrier和pause
(1) barrier
在Linux中称为内存屏障 ,所谓内存屏障,从处理器角度来说,是用来串行化读写操作的,从软件角度来讲,就是用来解决顺序一致性问题的。编译器不是要打乱代码执行顺序吗,处理器不是要乱序执行吗,你插入一个内存屏障,就相当于告诉编译器,屏障前后的指令顺序不能颠倒,告诉处理器,只有等屏障前的指令执行完了,屏障后的指令才能开始执行。
至于在那里插入读写屏障,要视软件的需求而定。读写屏障无法完全实现顺序一致性,但多处理器上的线程也不会一直盯着你的执行顺序看,只要保证在它看过来的时候,认为你符合顺序一致性,执行不会出现你代码中没有预料到的情况。所谓预料外的情况,举例而言,你的线程是先给变量a赋值,再给变量b赋值,结果别的处理器上运行的线程看过来,发现b赋值了,a却没有赋值,(注意这种不一致不是由缓存不一致造成的,而是处理器写操作完成的顺序不一致造成的),这时就要在a赋值与b赋值之间,加一个写屏障。
想深入了解,可以参看这篇文章:linux内核中的内存屏障
(2) pause
主要是提高性能和节省CPU耗电,pause就像nop,干的事情就是延迟空等的事情;详细可以参看:
__asm__("pause")用法
另外关于性能锁性能相关的文章参看: