首页 » 漏洞 » 谈谈存储软件的无锁设计

谈谈存储软件的无锁设计

 

面向磁盘设计的存储软件不需要考虑竞争锁带来的性能影响。磁盘存储软件的性能瓶颈点在于磁盘,磁盘抖动会引入极大的性能损耗。因此,传统存储软件的设计不会特别在意处理器的使用效率。曾经对一个存储虚拟化软件进行性能调优,在锁竞争方面做了大量优化,最后也没有达到性能提升的效果,原因就在于存储虚拟化的性能瓶颈点在于磁盘,而不在于处理器的使用效率。正因为如此,在面向磁盘设计的软件中,很多都采用单线程、单队列处理的方式,一定程度上还可以避免由于并发所引入的磁盘抖动问题。

在面向 NVMe SSD 设计的存储软件中,这一切正发生着变化。和传统磁盘相比, NVMe SSD 具有极高的 IO 读写性能,不存在传统磁盘所具有的访问寻道、抖动问题。为了发挥 NVMe SSD 的性能,无论在软件还是在硬件上都需要采用多队列技术,通过多队列方式充分发挥 NVMe SSD 的性能。通常 NVMe SSD 控制器可以提供 128 IO 提交和结束队列, NVMe 驱动会根据处理器的核数利用 SSD 控制器提供的硬件能力,实现 IO 的并发处理。在驱动层面,可以绑定处理器与 IO 队列,达到 IO 并发处理的效果。在这种情况下,业务线程一定要多线程并发利用处理器,才可以将 NVMe SSD 的多队列真正用起来。在很多情况下,业务的软件模型往往会分为计算单元和存储单元两部分。计算单元往往是多线程并发处理的模式;存储单元在传统磁盘上是单线程模式,在 NVMe SSD 上会变成多线程并发处理的方式。这种软件处理模型可以描述如下:

谈谈存储软件的无锁设计

在上述软件模型中,如果 Compute IO 单元通过合理的资源切分划分在一起, Compute IO 单元在同一个执行上下文中,那么整个软件的效率将会变得非常高。但是在很多情况下,计算和存储可以是两个软件模块,或者计算和存储资源无法有效的进行划分,那么在这种情况下,计算和存储线程之间需要通过消息队列进行通信。这种消息通信可以抽象成生产者 - 消费者模型。 Compute 单元是生产者; IO 单元是消费者。

生产者 - 消费者在实现过程中需要进行同步操作。如果采用传统的锁机制实现“生产者 - 消费者”队列,那么存储系统的性能瓶颈将会转移到锁竞争点。锁竞争一方面使得处理器处于长时间等待或者睡眠调度的状态;另一方面由于大量的缓存无效操作使得 CPU 的访存效率大大降低。总的来说,锁竞争将会导致 CPU 使用效率的降低,从而可以看到处理器的 IPC 指标变得很低。

那么在上述软件模型中,如何提升处理器效率,充分发挥 NVMe SSD 的性能呢?这就需要考虑采用无锁设计。在多线程并发处理的场景下,难免需要在线程之间进行数据交互,为了提高 CPU 的效率,这种大量的线程交互可以采用无锁的方式进行同步。无锁设计需要采用特殊的算法与处理器提供的特殊指令,常用的无锁算法有采用 CAS 指令实现的无锁队列,该种类型的算法需要一个 Dummy 节点,因此存在动态分配内存的问题。为了避免动态内存分配,可以采用固定长度的无锁队列 Lock-free Ring ,但是会存在队列深度固定的问题。在存储系统设计中,关键路径上最好需要避免内存资源的频繁分配,另外还需要实现队列深度的可变与 IO 数量的流控。因此,需要根据存储系统的特征,需要设计符合自身特点的无锁算法。在最近一个存储项目中,我们创造了一种无锁生产者 - 消费者及无锁流控算法,采用这种算法之后,我们可以发现 IO 的性能可以随着 CPU 核数线性变化。当 CPU 核数越多, IO 性能越高。下图是实际测试的性能与 IO 线程数量之间的关系图:

谈谈存储软件的无锁设计

谈谈存储软件的无锁设计

上图我们可以看出当 IO 线程数量增加到 28 之后,随机读性能变化不明显,并且出现了一定的性能下降。在该测试平台上,一共具备 28 CPU 硬核,通过超线程的方式,达到 56 CPU 核。从上面的实验结果来看,超线程对 IO 性能贡献不大。随机读性能在 IO Thread 数量达到 8 个之后,性能达到峰值。这是由于后端 NVMe SSD 盘性能达到了峰值。

NVMe 存储系统设计面临 CPU 性能瓶颈的问题,如何提高 CPU IO 处理效率是高性能存储系统设计必须要考虑的问题。无锁设计是提高 CPU 效率的一种有效手段,一个好的无锁设计需要与系统软件的设计整体考虑。

原文链接:谈谈存储软件的无锁设计,转载请注明来源!

0