论文导读 | 无锁并发哈希表

一、并发算法分类和常用方法介绍

1.分类

并发算法按照progress condition可以分为三类:

Wait-free:每个线程的每个操作在有限步内结束,无论其它线程的运行速度如何或者其它线程有没有崩溃。

Lock-free:至少有一个线程在取得进展,允许其它线程饥饿。

Blocking:不对进展作限制。

2.常用方法:

1) CAS (Compare And Swap)

CAS(X,c,d)是一个硬件提供的原子操作。它的意思是,我认为变量X现在的值是c,我想把它改成d。如果X确实是c,它就会被改成d,返回true;如果不是,就不改变它,返回false。比较大小和修改值的操作在一个原子操作内完成。

CAS操作有一个常见问题,叫ABA问题,解决方案是在要使用CAS的变量上加一个tag,每次CAS都对tag加一。具体例子如图。

2) CoW(Copy on Write)

因为CAS一般只能对8字节以下的连续内存区域进行操作,所以对于要修改大于8字节的需要CoW操作:复制要修改的内容到本地区域,然后做想要的修改,然后用CAS操作修改全局的指针即可。

三、具体文章介绍

前两篇是这个方向较经典的文章,只讲idea,后两篇新一些的文章会讲的详细一些。

1. High Performance Dynamic Lock-Free Hash Tables and List-Based Sets(2002)

这篇文章使用拉链哈希表的结构,采用无锁的单链表来实现并发。具体结构如上图。下图是无锁单链表insert和delete操作的步骤,都是使用CAS操作进行的。


2. Split-Ordered Lists: Lock-Free Extensible Hash Tables(2006)

上图是Split-Ordered Lists的结构。它的每个bucket对应一个dummy node,如图中虚线节点。一个KV对应一个实线节点。KV和bucket一起存在一个单链表中,单链表按照一个新定义的顺序split order排序。如果KV在某个bucket中,那么它在单链表中在这个bucket对应的dummy node和下一个dummy node之间。

这种哈希表有2^i个bucket,Hash function是 key%2^i。Split order指的是把每个KV的key(或者bucket的序号)的所有bit倒序,再把非dummy node得到的数的+1,把最终得到的数从小到大排序得到的顺序。Split order通过排序实现了对后i个bit的关注,实现了用单链表表示哈希表的“KV在bucket中”这件事。

Split-Ordered Lists是可以扩张的哈希表。扩张时对directory(即图中存放指向bucket的dummy node的指针的矩形结构)进行两倍的扩张,但不立即把新的bucket的dummy node插入新单链表中。等到有属于这个bucket的KV新加入这个哈希表,才会插入bucket的dummy node。下面四张图展示了插入新KV,并插入新dummy node的过程。


3. Lock-free Concurrent Level Hashing for Persistent Memory (2020) 

这篇文章是持久性内存上的哈希表Level Hashing(Pengfei Zuo, Yu Hua, and Jie Wu. Write-Optimized and High-Performance Hashing Index Scheme for Persistent Memory. In 13th USENIX Symposium on Operating Systems Design and Implementation (OSDI ’18), Carlsbad, CA, USA, October 2018)的无锁并发版本Clevel。上图展示了单线程的哈希表Level Hashing的结构。Level Hashing采用双层的bucketized cuckoo hashing的结构,在新增KV对应的bucket都满了且kick一次失败的情况下触发哈希表扩张。它双层结构的好处如下图,在哈希表扩张时只需要重新哈希1/3的KV即可。

下图是CLevel的结构。与Level Hashing不同,它有多层,且每个slot中存储的不再是KV本身,而是KV的指针,便于CAS。每层对应一个Level node,Level node串起来成为level list。Global context中存储了第一层和最后一层的level node的指针和这个哈希表是否正在resize的is_resizing 信息。

CLevel扩张分为5步:

i. Make a local copy of the global context pointer.

ii. Append a new level to the level list using CAS.

如果CAS失败了,说明其它线程也在做第二步。因此如果CAS失败了,我们直接去进行第3步即可。

iii. update the global context: set the first level and is_resizing using CoW

如果CAS失败了,说明其它线程在做第三或五步。因此如果CAS失败了,我们判断first level是否比自己线程的想改的first level大,如果大的话继续第四步,反之重新CAS直到成功为止。

iv. Rehash each item in the last level.

我们先把最后一层的KV复制到第一层,再删除最后一层的KV:我们允许KV的重复,但不允许KV的缺失。

v. update the global context: set the last level and is_resizing(when 2 levels left)

如果CAS失败了,处理方法和第三步同理。

由于第四步十分耗时,且不做完第4、5步不影响其它操作的顺利进行,因此我们将第4、5步交给后台的线程去做,这样可以避免增加前台操作的延时。哈希表扩张在哈希表只剩2层的时候停止。

下面两张图表示了在search, insert, update, delete操作时CLevel可能由于并发而遇到的问题,及其解决方法。

下图是CLevel随着线程数目增加的吞吐量变化。在线程数小于18的时候,它的加速比高,但大于18后就不高了。


4. A Efficient Wait-free Hash Table(2018)

这篇文章提出的哈希表的单线程版本是Extendible Hashing,结构如上图。Key的前缀决定了KV属于哪个bucket。

而这篇论文提出的无等待并发的哈希表结构及它在添加KV时的操作如下图所示,它把Extendible Hashing的两层结构扩展成了3层结构,以便并发。图中灰色部分代表新分配内存的结构。图中可以看到,更改每一块时用的都是前面提到的CoW方法,通过新分配内存和修改指针来实现对并发数据结构的修改。

下图是本文哈希表遵循的两个设计原则及其原因。这两个设计原则让本文哈希表在lookup多、resize少的真实workload下拥有较高的吞吐量。

论文的算法如下图所示:

下面我们说明为什么2轮循环即可使ApplyWFOp函数得到正确的结果(ResizeWF函数同理):如果两轮循环中任何一个循环的CAS成功,那么算法显然成功;我们担心的是两轮循环的CAS都失败的情况。

下图左边表示了2轮循环都失败的情况下,ApplyWFOp函数的关键步骤。两轮循环都失败,说明在第一轮循环和第二轮循环的read BState和CAS之间,各有来自其它线程的一次操作使得BState被改变。图中我们用bs2和bs5表示这两个BState。因此,一定有一个线程通过CAS将bs2改成了bs5.这个线程的read BState的时间一定晚于左边线程set toggle的时间(因为左边线程read BState读到的是bs0,但右边线程读到的是bs2),因此在右边线程的ApplyWFOp函数的iteration中,它已经做掉了左边线程想做的操作,因此ApplyWFOp函数一定成功。

下面是实验结果:

图中Lock是对每个bucket加读写锁的哈希表,LF-Split是上面提到的split ordered lists哈希表,LF-Freeze是当时SOTA的哈希表。

上图表示了在没有resize的情况下的实验结果,下图左边表示了有resize情况下的实验结果,右边表示了resize均摊在lookup和insert等操作中的实验结果。可以看到,本文哈希表通过遵守上面说的两个设计原则,在没有resize的情况下取得了很好的性能;但这是以resize很慢为代价的;但因为resize操作数目很少,均摊在其它操作中之后,本文哈希表整体性能还是不错的。

而且。本文是wait-free算法,对比的算法全是blocking或lock-free的算法,本文算法比比较的算法提供了更好的进度保证。本文算法和此前的wait-free算法相比,速度快很多。

三、总结

本文介绍了无锁并发哈希表的分类、常用方法、经典结构和2个新文章。无锁结构比起有锁结构,提供了更好的进度保证,且不容易阻塞,提高了加速比。但在实现中,无锁需要考虑的问题很多,比如怎么做空间回收,怎么避免或处理重复key的KV的情况等。有锁的方案设计起来相对简单,在冲突不多时也是一个很好的选择。

欢迎关注北京大学王选计算机研究所数据管理实验室微信公众号“图谱学苑“
实验室官网:https://mod.wict.pku.edu.cn/
微信社区群:请回复“社区”获取

实验室开源产品图数据库gStore:
gStore官网:http://www.gstore.cn/
GitHub:https://github.com/pkumod/gStore
Gitee:https://gitee.com/PKUMOD/gStore

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值