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