TSX(事务同步扩展)
TSX是新一代Haswell架构上,通过硬件支持的事务性内存(Transactional Memory)解决方案。HLE和RTM都在事务的基础上实现的硬件技术手段。一句话概括Intel的事务性同步扩展(Transactional Synchronization Extension, TSX)的动机:粗粒度锁保证的事务性操作,在高并发下性能下降,作为细粒度锁方案的一种替代,TSX通过硬件辅助保证正确性,编程更友好。
TSX overview
适用的场景:
一张有大量数据的表(原讲座中用银行账户记录做比),一种典型的事务性操作,是从其中一个条目a中减去一个数值x,加到条目b中:
[ a = a – x : b = b + x] (1)
如果用一个粗粒度的锁保护整张表的操作,在并发时会碰到如下的问题,比如同时另一个线程对不同的条目c和d操作,原本不冲突的操作,因为粗粒度锁的存在,不得不串行执行。可以想见,高并发下粗粒度锁的方案性能严重下降。
传统的优化手段是使用细粒度的锁,比如给表中的每一个条目单独加锁,那么上面存在的“假”的数据冲突就可以避免。但是细粒度锁极大增加的设计的复杂度,容易出现难以解决的bug。作为一个例子,考虑在(1)的操作同时,另一个线程进行如下操作:
[ b = b – x : a = a + x] (2)
由于a/b由两个独立的锁保护,完成(1)或(2)的操作,需要获得两把锁,如果(1)(2)不能完整获得两把锁,而是(1)获得lock(a),(2)获得lock(b),即出现死锁。所以细粒度锁的加锁解锁方案需要仔细设计,避免死锁和其他很多问题。
使用TSX的替代方案:逻辑上TSX是一把粗粒度的锁,将包含事务性的操作的critical section包起来;由硬件自动检测操作中的数据冲突,保证事务性操作的正确性,发掘操作间的并行性,实现上类似每个条目都有细粒度的锁,这被称作lock elision。
不适用的场景:
从上面的例子可以看出,TSX主要解决的是粗粒度锁下的“假”数据冲突问题,如果原本不需要细粒度的锁,或者产生冲突的条目少,“真”冲突概率高,那么使用TSX的收益不大。TSX不是银弹。
Q: 我怎么知道什么时候该使用TSX?
A: 如果现在的程序没有性能问题,你可以去休息,喝杯咖啡;如果有我上面场景中的性能问题,你可以试试TSX,用起来也很方便。
性能
典型应用场景下,相对粗粒度锁的方案,TSX的方案在高并发下的性能有明显提升,可以达到接近线性的可扩展性;相对细粒度锁的方案,TSX在高并发下的性能也有小的优势(TSX的开销可能比细粒度锁的开销小)。图不方便贴了。
相比比较火的无锁编程,TSX也有明显的优势。无锁编程不是真的没有锁,而是很强的依赖于原子操作,它的劣势是限制了数据结构(只能用队列等),难于设计和优化,高并发下也有问题。TSX下数据结构的选择更自由(因为使用的是和粗粒度锁一样的临界区的模型),同样的需求用无锁编程难以验证正确性。
底层TM跟踪事务的read-set 和write-set ,以一个64字节的高速缓存行为粒度。这里的read-set 和write- set 分别表明事务在执行过程中已读出或写入的所有高速缓存行。 如果事务的read-set中的一个高速缓存行被另一个线程写入,或者事务的 write-set 中的一个高速缓存行被另一个线程读取或写入,则事务就遇到冲突(conflict)。 对于那些熟悉TM术语的人而言,这就是所谓的强隔离(strong isolation),因为一个非事务性(non-transactional)内存访问可能会导致事务中止(abort)。 冲突通常导致事务中止,且在一个高速缓存行内还可能发生假冲突(falseconflicts)。
TSX可以允许事务相互嵌套,在概念上是通过将嵌套展平成单个事务来处理的。 然而,嵌套的数量有一个具体实现特定的限制,超过此限制将导致中止。 在一个嵌套事务内的任何中止,都将中止所有的嵌套事务。
事务只能使用可高速缓存的回写内存操作(write-back cacheable memory operations),但可以在所有的权限级别下使用。不是所有的x86指令都可安全地用于事务内。 有几个x86指令将导致任何事务(对于HLE或 RTM)中止,特别是CPUID和PAUSE。
此外,在特定的实现中,还有些可能导致中止的指令。 这些指令包括x87和MM