K8s绑核调度优化,银河麒麟操作系统助力国产芯片性能更佳
Kubernetes(简称K8s)是业界主流的开源容器编排平台之一。kubelet作为管理K8s中Pod及其相关容器的核心组件,与容器运行时对接。其原生的static policy绑核机制旨在避免线程在不同die之间轮转,从而减少跨die仿存的性能开销。由于飞腾S2500 L2缓存设计特殊,在银河麒麟操作系统下,kubelet原有的绑核机制难以发挥预期效果,影响CPU性能的发挥。麒麟软件技术团队对此进行了优化,使K8s的容器在飞腾S2500平台上性能更佳。
CPU绑核
CPU绑核是借助Cgroup cpuset子系统将线程绑定到指定CPU core上执行。合理的CPU绑核避免了内核CPU调度线程带来的性能开销。
本文主要关注L2缓存Miss产生的时间开销。如图1所示:
图1
图中展示了线程在CPU core之间调度的场景。在阶段1,线程A和B的数据加载到各自核心的L2缓存。若未进行绑核,在阶段2,线程A和B可能交换运行的CPU core,导致L2缓存数据更新,造成cache miss,带来性能开销。而进行绑核操作则始终维持在阶段1状态,避免L2缓存数据更新,提升性能。
飞腾S2500的硬件特点
通常CPU core与L2缓存是一对一的关系(见图1),而S2500每4个CPU core共享一个L2缓存。引发了新的问题:若同一个L2缓存中的4个CPU core绑定不同任务线程,这些线程在运行过程中会争抢L2缓存,造成性能波动,使得绑核效果无法完全发挥。
在典型x86 CPU环境下,假设有8个CPU core,两个任务,每个任务有两个线程,一种可能的绑核情况如下图所示,每个core有独立的L2缓存,因此不同任务的线程不会发生L2缓存抢占,CPU绑核完全发挥效果。
图2
在飞腾S2500服务器中,在与图2相同的CPU绑核情况下,任务1和任务2仍会出现L2缓存争抢,触发L2 Cache Miss,导致某个任务性能波动,绑核的优化效果未能充分发挥。
图3
一种理想的绑核情况如下图,任务1和任务2的线程各自绑核到共享同一个L2缓存的CPU core上。由于同一个任务的线程用的数据相近,L2缓存完全为该任务服务,减少Cache Miss,降低性能波动。此时能够达到和图2相同的绑核效果。
图4
基于飞腾S2500的K8s绑核调度
优化方案(L2亲和性优化)
原生kubelet代码中,在绑核过程中默认执行takeByTopologyNUMAPacked()函数。该函数按照CPU层次结构从高到低,先尝试分配整个NUMA节点或者socket的CPU core,然后尝试分配整个CPU core中的计算资源(考虑超线程,一个CPU核提供两个超线程),最后分配剩余的CPU core(超线程)。该分配逻辑未涉及L2层面的分配算法,因此原生k8s会如前文所示,在飞腾S2500上无法完全发挥绑核的效果。
基于飞腾S2500的L2缓存特殊性,麒麟软件技术团队对kubelet绑核调度代码增加L2亲和性优化:
• 1)在原生kubelet代码分配整个NUMA节点或Socket和分配整个CPU core的算力间,插入L2层级分配逻辑;
• 2)绑核时优先选择空闲的L2缓存;
• 3)同一任务线程尽可能绑核在共享同一个L2缓存的CPU core上。
其中,1)继承了kubelet原生代码的特性;2)和3)保证了同一任务线程能够尽可能独占L2缓存,在绑核时尽可能避免不同任务线程争抢L2缓存导致的性能波动。
修改后的takeByTopologyNUMAPacked()函数逻辑如图5所示:
图5
例如,分配8个CPU core用来绑核。原生kubelet在飞腾S2500平台上的绑核结果可能如图6所示:先将NUMA1中空闲的CPU core分配完,再从NUMA2中选择一个CPU core进行分配。
图6
优化后的kubelet分配结果如图7所示:待分配的CPU core尽可能独占L2缓存。
图7
经过测试,优化后的kubelet代码能够满足要求,绑核后提升线程运行效率和稳定性。以4C4G为例,在已有CPU资源被其他Pod占用的情况下部署新Pod,测试Unixbench指标性能对比,如图8所示:
图8
总结
近年来,随着网信事业的快速发展,国产软硬件产品在技术和品质上都取得了显著的进步,以飞腾、鲲鹏、龙芯、兆芯、海光、申威等为代表的国产CPU性能也得到大幅提升。麒麟操作系统已全面支持国产主流CPU,在系统安全、稳定可靠、好用易用和整体性能等方面具有领先优势。未来,麒麟软件将与产业链伙伴共同携手,为我国行业信息化及国家重大工程建设提供安全可信的操作系统支撑。
通讯员 | 魏玉成
来 源 | 服务器研发部、产品管理部
审 核 | 市场与政府事务部
热点推荐