CAS原子操作实现无锁及性能分析
-
Author:Echo Chen(陈斌)
-
Email:chenb19870707@gmail.com
-
Date:Nov 13th, 2014
最近在研究nginx的自旋锁的时候,又见到了GCC CAS原子操作,于是决定动手分析下CAS实现的无锁到底性能如何,网上关于CAS实现无锁的文章很多,但少有研究这种无锁的性能提升的文章,这里就以实验结果和我自己的理解逐步展开。
-
1.什么是CAS原子操作
在研究无锁之前,我们需要首先了解一下CAS原子操作——Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作,X86下对应的是 CMPXCHG 汇编指令。
大家应该还记得操作系统里面关于“原子操作”的概念,一个操作是原子的(atomic),如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。有了这个原子操作这个保证我们就可以实现无锁了。
CAS原子操作在维基百科中的代码描述如下:
1: int compare_and_swap(int* reg, int oldval, int newval)
2: {
3: ATOMIC();
4: int old_reg_val = *reg;
5: if (old_reg_val == oldval)
6: *reg = newval;
7: END_ATOMIC();
8: return old_reg_val;
9: }
-
也就是检查内存*reg里的值是不是oldval,如果是的话,则对其赋值newval。上面的代码总是返回old_reg_value,调用者如果需要知道是否更新成功还需要做进一步判断,为了方便,它可以变种为直接返回是否更新成功,如下:
-
1: bool compare_and_swap (int *accum, int *dest, int newval)
2: {
3: if ( *accum == *dest ) {
4: *dest = newval;
5: return true;
6: }
7: return false;
8: }
除了CAS还有以下原子操作:
- Fetch And Add,一般用来对变量做 +1 的原子操作。
1: << atomic >>
2: function FetchAndAdd(address location, int inc) {
3: int value := *location
4: *location := value + inc
5: return value
6: }
Test-and-set,写值到某个内存位置并传回其旧值。汇编指令BST。1: #define LOCKED 1
2:
3: int TestAndSet(int* lockPtr) {
4: int oldValue;
5:
6: // Start of atomic segment
7: // The following statements should be interpreted as pseudocode for
8: // illustrative purposes only.
9: // Traditional compilation of this code will not guarantee atomicity, the
10: // use of shared memory (i.e. not-cached values), protection from compiler
11: // optimization, or other required properties.
12: oldValue = *lockPtr;
13: *lockPtr = LOCKED;
14: // End of atomic segment
15:
16: return oldValue;
17: }
- Test and Test-and-set,用来实现多核环境下互斥锁,
1: boolean locked := false // shared lock variable
- Fetch And Add,一般用来对变量做 +1 的原子操作。