主要观点:
- 构建分配器时,为解决多线程同时使用导致锁竞争激烈的问题,TCMalloc 提出线程本地缓存块的方法,后来改为使用 per-CPU 数据,这种方式对 CPU 缓存友好且无原子操作。
- 介绍在现代 Linux 上构建 CPU 本地数据结构的方法,包括使用 Futexes 和 Restartable sequences 等内核原语,通过特定的系统调用和汇编代码实现 per-CPU 编程。
- 以创建“Checkout Desk”为例,实现一个 per-CPU 的数据结构,用于缓存页面缓存,避免频繁的锁获取和释放操作,提高性能,同时要注意避免“false sharing”问题。
关键信息和重要细节:
- 内核原语:Futexes 是经典的“cas-with-the-kernel”系统调用,Restartable sequences 是用于 per-CPU 编程的原语,
rseq(2)
系统调用用于注册和管理 per-CPU 数据,其签名包含Rseq
结构体和相关参数,返回值表示成功与否。 - 创建 Restartable sequence:需要组装
CritSec
结构体描述重启序列,将其地址存储到RSEQ.crit_sec
中,完成后要重置该字段,在组装过程中要注意一些细节,如使用特定的标签和指令,避免链接错误等。 - “Checkout Desk”实现:
PerCpu<T>
结构用于存储 per-CPU 的数据,为避免“false sharing”,将指针存储在 64 字节对齐的缓存行中,初始化时要获取系统的逻辑核心数,checkout()
函数实现了获取和归还缓存的操作,在重启序列中进行指针交换,若指针为空则分配新的缓存行。
总结:通过使用 per-CPU 数据结构和相关的内核原语,能够提高多线程环境下的程序性能,避免锁竞争和缓存问题,但实现过程较为复杂,需要注意各种细节和平台差异。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。