本文档介绍了名为“Shenango”的系统研究,该研究由Amy Ousterhout、Joshua Fried、Jonathan Behrens、Adam Belay及Hari Balakrishnan(均来自MIT CSAIL)共同完成,并于2019年2月26-28日在美国波士顿举行的第16届USENIX网络系统设计与实现研讨会(NSDI ‘19)上发表,收录于该会议论文集。这是一项针对数据中心场景下低延迟、高吞吐量和高CPU效率需求的原创性系统研究。
在数据中心应用场景中,如Memcached等对延迟极其敏感的应用,要求系统能够提供微秒级的尾延迟和极高的请求处理速率。同时,这些应用的负载变化剧烈,可能在从分钟到微秒的多个时间尺度上呈现高方差。当前的系统解决方案在此类需求下面临严峻挑战。传统的Linux内核由于其较高的开销,只能在CPU利用率保持较低时支持微秒级延迟,导致大量计算资源闲置。另一种方案,即内核旁路网络堆栈,如Zygos,通过让专用核心以自旋轮询方式访问网卡来实现低延迟,但这种方法严重浪费CPU:即使平均负载不高,也必须为预期的峰值负载预留足够的核心,导致在非峰值时段CPU效率低下。现有系统的核心再分配机制(如Linux约每4毫秒,Arachne约每50-100毫秒)过于迟缓,无法快速响应微秒级的负载突发。因此,如何在维持微秒级尾延迟的同时,实现高CPU效率,成为数据中心系统领域的一个开放性问题。
针对上述挑战,Shenango的研究目标是:1)为数据中心应用实现微秒级的端到端尾延迟和高吞吐量;2)在多核机器上实现CPU高效的应用封装;3)通过同步I/O、轻量级线程、阻塞式TCP网络套接字等标准编程抽象,保持高开发者生产力。其核心创新在于,能够以极细的粒度(每5微秒)在运行于同一机器上的不同应用之间动态重新分配CPU核心,将延迟敏感型应用未使用的计算周期高效地分配给批处理应用使用。
Shenango的工作流程主要围绕其两个核心组件展开:运行在专用核心上的特权组件IOkernel,以及作为库链接到每个应用地址空间中的用户态运行时。
首先,Shenango的整体架构由IOkernel进行中央协调。IOkernel持续忙轮询,执行两项关键功能:网络I/O数据平面操作与核心分配。在网络数据面,它直接轮询网卡接收队列,将每个到达的数据包基于RSS哈希放入相应应用核心的共享内存入站包队列;同时,轮询各运行时核心的出站包队列,将数据包转发给网卡。在核心分配方面,IOkernel以5微秒为间隔,运行一个高效的拥塞检测算法。该算法通过检查每个应用运行时中每个活跃核心上的两个队列状态:轻量级用户线程运行队列和入站数据包队列。如果发现某个队列中的某个项目(线程或数据包)在连续两次检测中均存在(即排队时间至少达到5微秒),则认为该应用遇到了“计算拥塞”,IOkernel会尝试为其分配一个额外的核心。这种基于排队时长而非队列长度的检测机制更为鲁棒,无需针对不同请求时长调整阈值参数。核心分配决策由核心选择算法执行,该算法综合考虑超线程效率(优先将同一物理核心的超线程伙伴分配给同一应用)、缓存局部性(优先分配应用最近使用过的核心)和延迟(优先分配空闲核心,其次才抢占正在“突发”使用超额核心的其他应用的核心),以做出最优选择。一旦选定核心,IOkernel会唤醒目标应用运行时的一个已“停放”的核心线程,并将其绑定到新分配的核心上。
其次,每个应用运行在各自的运行时环境中。运行时负责提供高层次的编程抽象,如轻量级用户线程、互斥锁、条件变量和阻塞式TCP/UDP网络套接字。运行时在启动时会创建一组核心线程(kthreads),每个kthread管理一个本地运行队列,用于调度用户线程(uthreads)。运行时内部采用工作窃取策略进行负载均衡:当某个kthread的本地运行队列为空时,它会尝试从其他活跃kthread那里窃取用户线程来执行,这有助于降低尾延迟,特别是在服务时间变化大的工作负载中。当kthread找不到任何工作可做时,它会自愿“停放”,将核心交还给IOkernel。运行时还负责完整的网络协议栈处理(包括TCP),支持跨核心的包处理工作窃取,这比Zygos只能在应用层进行工作窃取提供了更细粒度的负载均衡能力,但可能引入短暂的包重排。研究表明,这种重排比例很低,重排序开销很小。
为了评估Shenango的性能,研究团队设计并执行了一系列实验。实验环境为配备12核Intel Xeon CPU、10 Gb/s网卡的服务器。对比系统包括Arachne(用户级线程系统)、Zygos(先进的内核旁路网络堆栈)和Linux。评测的应用包括Memcached键值存储、模拟不同服务时间分布的微服务(spin-server)以及DNS服务器(gdnsd)。同时,系统还会运行一个批处理应用(Parsec的swaptions)来利用空闲周期,以测量CPU效率。
实验的主要结果如下:在Memcached测试中,Shenango在维持与Zygos相当的尾延迟(99.9%延迟低于100微秒)和吞吐量(超过500万请求/秒)的同时,能显著提升CPU效率。Zygos需要预留所有核心给Memcached才能达到峰值吞吐量,而Shenango可以将未使用的核心线性地分配给批处理应用,实现吞吐量的线性交换。Linux和Arachne由于内核网络栈开销或核心分配速度慢,其吞吐量和尾延迟表现远逊于Shenango和Zygos。在模拟不同服务时间分布(恒定、指数、双峰)的spin-server测试中,Shenango再次展示了其优势:它在所有分布下都能维持低尾延迟,并能根据负载变化动态调整核心分配,将空闲周期高效用于批处理。而Zygos无法支持批处理,Linux和Arachne则因尾延迟高或吞吐量低而表现不佳。在突发负载测试中,Shenango展示了其快速反应能力。当请求率在1秒内从10万/秒瞬间跃升至500万/秒时,Shenango的IOkernel能在微秒级内检测到拥塞并分配核心,使得99.9%尾 latency 仅略微上升并迅速稳定。相比之下,Arachne因其核心分配间隔长达50毫秒,在负载突变后需要数百毫秒才能响应,导致尾延迟飙升至毫秒级,并积累大量待处理请求队列。微基准测试进一步揭示了Shenango组件的效率:其线程库操作(如yield、条件变量、线程生成)的延迟优于或媲美Go和Arachne;网络栈和核心分配开销很低,唤醒或抢占一个核心线程仅增加几微秒的往返时间;其跨核心包处理工作窃取机制在连接数少、负载不均衡时,能比Zygos更有效地维持低延迟;最后,实验证实了5微秒核心分配间隔对于维持低尾延迟至关重要,更长的间隔会导致尾 latency 显著上升。
Shenango的研究得出结论:通过引入一个运行在专用核心上的IOkernel,并结合一个高效的微秒级拥塞检测与核心分配算法,Shenango系统成功地解决了数据中心环境中低尾延迟与高CPU效率之间的核心矛盾。它首次实现了在维持微秒级尾延迟的同时,能够以微秒粒度在多个应用间动态复用CPU核心,从而将原本被静态预留或忙轮询浪费的CPU周期有效地用于批处理等后台任务,大幅提升了整体资源利用率。
本研究的亮点和价值在于:1)核心创新性:提出了首个能够在微秒时间尺度上动态、高效地重新分配CPU核心的系统,解决了该领域的长期挑战。2)性能突破:在保持与最先进内核旁路网络栈(Zygos)相当的低延迟和高吞吐量的同时,实现了数量级更高的CPU效率。3)实用性兼顾:在提供高性能的同时,通过用户态运行时保留了轻量级线程、阻塞式套接字等标准同步编程抽象,提升了开发者的生产力,避免了事件驱动等复杂编程模型。4)方法有效性:基于排队时长(而非队列长度或历史利用率)的拥塞检测算法简单而高效;将数据平面(包转发)与控制平面(核心分配)在IOkernel中深度集成,实现了快速决策与低开销执行。5)广泛适用性:研究不仅验证了Shenango在Memcached上的性能,还通过不同服务时间分布、突发负载、UDP应用等多种场景测试,证明了其设计原则的普适性和鲁棒性。这项研究为未来数据中心操作系统的设计提供了新的思路和强有力的技术基准,对于降低大规模数据中心运营成本、提升硬件资源利用率具有重要的科学价值与应用潜力。