在 Valkey8.0 中引入异步 IO 线程提高并行度,并且将更多的工作转移到 IO 线程,将主线程执行的 I/O 操作量降至最低,此时,经过测试,单个 Valkey 节点每秒处理请求可达 80W。
通过分析开启 IO 线程后 Valkey 性能,主线程大部分时间都花销在访问内存查找 key,这是因为 Valkey 字典是一个简单但低效的链式哈希实现,在遍历哈希链表时,每次访问 dictEntry 结构体、指向键的指针或值对象,都很可能需要进行昂贵的外部内存访问。
于是在 Valkey8.0 中引入了 ** 数据预取(Prefetch)和内存访问分摊(MAA)** 技术,进一步提升 Valkey 单节点访问性能。
数据预取(Prefetch)
随着摩尔定律在过去 30 年间的持续生效,CPU 的运算速度大幅提升,而存储器(主要是内存)的速度提升相对较慢,这导致了存储器与 CPU 之间的速度差异。当 CPU 执行指令时,如果需要从内存中读取数据或指令,由于存储器速度的限制,CPU 可能需要等待访问存储器操作完成,从而导致性能瓶颈。
为了解决访问存储器瓶颈这一问题,现代计算机系统采用了多级缓存及内存层次结构,包括 L1、L2、L3 缓存以及主存等。尽管高速缓存(Cache)能够提供更快的访问速度,但其容量有限,当 CPU 访问的数据无法在高速缓存中找到时,就需要从更慢的内存层级中获取数据,这会导致较高的访问延迟,并降低整体性能。
数据预取(Prefetching)技术可以在一定程度上解决访问存储器成为 CPU 性能瓶颈的问题。数据预取是一种提前将数据或指令从内存中预先加载到高速缓存中的技术。通过预取,CPU 可以在实际使用之前将数据预先加载到缓存中,从而减少对内存的访问延迟。这样可以提高访问存储器的效率,减少 CPU 等待访问存储器的时间,从而提升整体性能。
__builtin_prefetch () 是 gcc 编辑器提供的一个内置函数,它通过对数据手工预取到 CPU 的缓存中,减少了读取延迟,从而提高程序的执行效率。
在 Valkey8.0 中,主线程在执行命令之前,通过使用 __builtin_prefetch () 命令,对所有即将操作的命令参数、key 及对应的 value 进行批量预取,提高主线程执行命令的效率。
内存访问分摊(MAA)
内存访问摊销 (MAA) 是一种旨在通过降低内存访问延迟的影响来优化动态数据结构性能的技术。它适用于需要并发执行多个操作的情况。其背后的原理是,对于某些动态数据结构,批量执行操作比单独执行每个操作更高效。
这种方法并非按顺序执行操作,而是将所有操作交错执行。具体做法是,每当某个操作需要访问内存时,程序都会预取必要的内存并切换到另一个操作。这确保了当一个操作因等待内存访问而被阻塞时,其他内存访问可以并行执行,从而降低平均访问延迟。