3.3.1. SMT#

3.3.1.1. SMT的挑战与解决思路#

SMT(Simultaneous Multi-Threading,超线程技术,对应X86的HT)的核心目标是提升处理器的资源利用率和并行计算效率:

  1. 提升闲置资源利用:现代处理器(如X86和鲲鹏920B)集成了超标量、乱序运行、大量的 寄存器及寄存器重命名、多指令解码器、预测运行等特性,这些特性的原理是让CPU拥有 大量资源,并可以预先运行及平行运行指令,以增加指令运行效率,可是在现实中这些 资源经常闲置,为了有效利用这些资源,就干脆再增加一些资源来运行第二个线程,让 这些闲置资源可运行另一个线程。从而提高处理器流水线资源利用率。例如,Intel在 奔腾4发布的时候说过,超线程技术仅增加约5%的芯片面积,可实现15%-30%的整机性能提升。 Nehalem架构带来全新的超线程技术,得益于指令集分支预测技术与较短的流水线,它拥有 比奔四更好的效能,再加上整合内存控制器让其具有更大的内存带宽,还有更大的 缓存,这样就能更有效的发挥超线程的作用,因此Nehalem的超线程可以在增加很少能耗 的情况下,让性能提升20%-30%,后续每一代虽然都有一些小修改,不过基本上都是Nehalem 架构的延续。

  2. 提升并行计算效率:在多线程应用场景中,超线程允许操作系统同时调度多个任务,减少 线程切换的开销(软件切换上下文),提升吞吐量。

SMT的设计在大部分情况下能带来约20%的整机性能收益,但不同应用和不同负载下流水线资源的竞争情况不一样,会导致应用性能抖动,在云原生场景带来不确定性。典型的几个问题和现象有:

整机不同负载下容器利用率不一致:

同样一个4U的容器调度到整机利用率只有10%的物理机上,容器的CPU利用率是50%(2U),如果调度到整机利用率60%的物理机上,容器的CPU利用率可能就是80%(3.2U)。本质上是由于低负载场景下操作系统调度容器使用的都是物理核,但高负载场景下使用的基本都是SMT,运行相同任务在SMT模式下需要的时间更长。这会在运维上带来麻烦。

优化措施:

通过在linux调度器中追踪SMT上同时有任务运行的情况,按经验值(一般情况下同时运行只有独占物理核60%的算力)将CPU运行时间片进行归一化处理。步骤如下:

  1. 跟踪相邻SMT同时运行的时间:ptime

  2. 跟踪当前SMT运行但邻居SMT处于idle的时间:delta_exec – ptime

  3. 利用相同的公式计算出任务在SMT上运行的有效时间:rtime

在更新当前任务所在cgroup的CPU利用率时,使用有效时间rtime,而非墙上时间delta_exec。

整机不同负载下容器性能不一致

相同规格容器在不同整机负载物理机上性能不一致,本质上跟利用率不一致是同一个问题的两种不同的现象。但从性能维度看影响更大,Flink这样的集群应用,某一个容器性能降低可能导致集群性能整体降低。如需要4U物理核算力的容器,在高负载场景下只得到了2U的等效算力是触发了cgroup的quota限制,导致性能受损。

优化措施:

  1. 操作系统中动态补偿cgroup时片,在内核调度器更新任务调度状态时,用任务运行的有效时间(超线程同时运行rtime按60%来折算)来更新任务的vruntime:Curr->vruntime += delta_exec - rtime;并且补偿cgroup下任务的时间配额:tg->runtime += delta_exec – rtime。

  2. 在K8S下发POD资源使用量的时候根据服务器是否有超线程进行折算(阿里方案),这样资源调度不够精细。

在线-离线混部离线任务SMT性能干扰大:

在线-离线混部中需要优先保障在线任务的QoS,离线一般是高负载型任务,在处理器流水上对在线任务争抢更激烈。

优化措施:

内核调度机制上要求在线任务运行时,同一对SMT上不能有离线任务在运行,如果有离线任务就将其驱离走。

  1. 任务占用CPU运行前,通过给邻居SMT核发送一个IPI,让这个SMT核强制进入idle,并且忽视队列上所有处于runnable的任务。

  2. 任务放弃CPU运行前,通过给邻居SMT核发送一个IPI,让这个SMT核重新检测队列里是否有runnable的任务。

缺点:

  1. 需要触发IPI引入开销

  2. 邻居SMT进入idle有滞后性,仍然会在线任务有干扰

  3. 邻居核被迫idle,降低了整体的CPU利用率。

不同类型任务跑到一对SMT上性能收益不确定:

前面针对SMT问题的描述和优化都是基于时间片维度和经验(SMT同时运行,单SMT算力是物理核的60%)数据的。但不同应用在SMT上互相干扰程度是不一样的,如访存密集应用会对后端造成更大的压力和竞争,而计算密集型应用程序则会对算术单元造成压力。这意味着应用程序间干扰将严重依赖于共同运行的应用程序的资源需需求类型。

优化措施:

采样PMU微架构事件,通过机器学习算法判断应用特征,将互补的应用调度到同一对SMT线程上运行。