lockfree

为什么要lockfree

按我的理解, lockfree就是不去 调用操作系统给定的锁机制.

1. 会有system call,  and system call is expensive; 比如pthread在linux里调用了futex.

2. 会失去控制, 接下来发生什么看操作系统心情了.

 

怎么做lockfree

不用操作系统给定的 同步机制, 而使用 硬件提供的机制, 简单说, 就是对 机器指令 的封装函数(right?).

 

首先一个是 compare-and-set. CAS/TAS

保证对某个变量的读出和修改是 原子的. 如下:

typedef LONG volatile tsl_t;

#define    MUTEX_SET(tsl)    (!InterlockedExchange((PLONG)tsl, 1))
#define    MUTEX_UNSET(tsl)    InterlockedExchange((PLONG)tsl, 0)
`MUTEX_SET`返回1 表示已经锁住: 
若此时 `tsl`为1, 表示已经被锁了, 置`tsl`为1, 返回原值的!, 即为0;
若此时 `tsl`为0, 表示锁可用, 置`tsl`为1, 返回1, 成功.
`MUTEX_SET`会被放入一个 loop中不断重试.

在gcc中, 可以用类似 `__sync_bool_compare_and_swap`的原语:
https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

对应不同的硬件/编译器, 可以使用的cas不同. 例子: bdb: mutex_int.h. wiredtiger: gcc.h/msvc.h

 

memory barrier解决乱序

出于性能考虑, 编译器/硬件 会 reorder execution of your program. 对单线程程序 完全没有影响, 多线程就不行了.

编译器会reorder: 这个算内部矛盾? 毕竟都是软件的事

硬件(cpu) 会reorder: 对cpu来说, 数据放在三个地方: 寄存器/cpu缓存/主存.  

   LOAD - 从主存读入 到寄存器/缓存

   STORE - 从寄存器/缓存 写入主存 

本来 有 MESI - 缓存一致性协议(cache coherency) 保证 缓存-主存的一致性的:

https://en.wikipedia.org/wiki/MESI_protocol

   modified - exclusive - shared - invalid

MESI是一个理论上的东西. 在实现上, 从性能考虑, 加入 store buffers, invalidate queues.

store buffer:

     要解决的问题: 写一个shared/invalid cache line, 先广播 read-invalid message, 让别的处理器都invalid, 返回Invalidate Acknowledge, 然后才能写入cache.

     解决方案:可以写入store buffer, 广播, 当前processor 接着做别的. 等收到所有处理器都invalid完了, 返回Invalidate Acknowledge, , 在写入cache. (异步执行的意思?)

     给程序带来的新问题: write离开 buffer的时间 和 异步执行有关. 和程序原order无关.

 invalidate queue:

     要解决的问题: invalid 一个cache line挺慢的; 万一store buffer满了, processor 需要等待 某write释放 才能继续.

     解决方案: 把 invalid 请求 入queue, 立即发送Invalidate Acknowledge, 然后慢慢执行queue. (还是异步?)

     给程序带来的新问题: 

Memory Barrier

A store barrier will flush the store buffer, ensuring all writes have been applied to that CPU's cache.

A read barrier will flush the invalidation queue, thus ensuring that all writes by other CPUs become visible to the flushing CPU.

在做下一次store前, 保证store buffer被处理完 - membar之前的写 全部在cache里;

在做下一次load前, 保证invalidation queue被处理完 - 保证cache中没有过期数据.

full membar

read membar: flush invalidation queue

write member: flush store buffer

 

LFENCE/SFENCE/MFENCE

acquire/release

LoadLoad/LoadStore/StoreLoad/StoreStore: sparc

 

 

 

 

volatile: 对c来说, 用于阻止编译器过度优化. 如 替换 循环变量为 `while(true)` -- (这个我见过!); 改内存访问为 寄存器访问等.

 

http://lwn.net/Articles/250967/

http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xbd_chap04.html#tag_21_04_11

http://www.puppetmastertrading.com/images/hwViewForSwHackers.pdf

posted @ 2016-08-17 16:27  brayden  阅读(462)  评论(0编辑  收藏  举报