谈CPU共享场景下的性能问题

简介: 背景容器化场景下,K8S (Kubernetes)调度器主导了单机上的资源布局,随之引入的是越来越多的多应用共享CPU场景,本文借着分析CPU共享的一些特征,引出其中存在的性能问题,并分析一些现有解法的思路和不足,做出展望。CPU的独享与共享OS提供Cgroup子系统以供用户进行资源管控,其中和CPU配置相关的有CPU和CPUSET两个子系统。 通常情况下一个容器内的应用会同时隶属于这两个子系统,

背景

容器化场景下,K8S (Kubernetes)调度器主导了单机上的资源布局,随之引入的是越来越多的多应用共享CPU场景,本文借着分析CPU共享的一些特征,引出其中存在的性能问题,并分析一些现有解法的思路和不足,做出展望。

CPU的独享与共享

OS提供Cgroup子系统以供用户进行资源管控,其中和CPU配置相关的有CPU和CPUSET两个子系统。

 

通常情况下一个容器内的应用会同时隶属于这两个子系统,并分别拥有一个对应的Cgroup子组。子组内提供了各个用于控制CPU使用的配置接口,比较关键的有:

  • cpuset.cpus

该组的CPU使用范围

  • cpu.cfs_quota_us & cpu.cfs_period_us

该组的CPU使用时间在每cfs_period_us内不会超过cfs_quota_us

  • cpu.shares

该组的share权重,和该组同层所的share权重之和的比值,即是最坏情况下在该层所占有的CPU时间比

 

所谓的CPU独享,就是通过配置cpuset.cpus来配置独占的CPU范围,容器之间没有CPU重叠。而CPU共享则是复数个容器配置相同的cpuset.cpus,并通过配置cpu.shares来控制各个容器使用这些CPU的比例。

需要注意的是,由于该比例仅在发生CPU竞争的时候生效,为了控制在没有竞争情况下容器的CPU使用量,还需要通过配置cpu.cfs_quota_us & cpu.cfs_period_us来进行限制。

共享CPU的特征

由于独享CPU独享的先入为主,谈共享CPU性能问题的时候,往往是对比于独享CPU而言的。要了解这些问题,必须对共享CPU后负载的行为特征有所了解。

首先,多个应用分享自己的CPU给彼此,会形成更大的CPU活动范围,原先独享方式下跑在小范围CPU内的任务,现在会在更大的活动范围里运行,这引入了第一个问题,即运行范围NUMA跨节点导致的性能问题。

其次,由于活动范围的增加,一个应用内的进程和线程可以被并发处理的能力提升了,但由于有quota的限制,并发度越高,消耗quota就越快,任务就会更快的在period内进入限制运行状态,这就引入了第二个问题,即并发度变化带来的性能问题。

最后,由于共享CPU,应用之间的竞争就会出现,时间片机制只能确保竞争的合理,无法消除竞争。竞争会带来两个问题,一是上下文切换,二是缓存刷新,由于整机负载规模没有发生变化,一定比例的上下文切换从应用自身任务的相互切换,变成了不同应用任务之间的切换,由于不同应用之间共享内存的概率较低,这就引入了第三个问题,即Cache亲和性下降带来的性能问题。

即然共享CPU存在那么多问题,为什么我们还越来越多的使用这种部署方式呢,原因是在于共享方式可以提升CPU资源的效能。当一个应用没有用满独享CPU的时候,这些资源是被浪费的,而共享方式就可以把这部分资源用在别的需要的应用上,不至于让CPU白白空闲。

问题分析

上面通过分析共享CPU的特征,引出了三个性能问题,接下来具体分析下这些问题。

问题I —— NUMA跨节点

在NUMA场景下,当任务的CPU使用范围超过一个NUMA节点,就会引入跨节点问题,即当任务运行在NUMA节点X的CPU上,其操作的内存却存放在NUMA节点Y上时,会触发远端内存访问,其写效率取决于X和Y的距离,距离越远、内存操作越重,性能下降的越明显。

通过perf工具可以抓取远端内存的访问次数,比较独享和共享两种配置下,单位时间内的访问次数差别,就可以确认跨节点问题的出现,通过观察numactl工具提供的节点距离,就可以估算出性能下降的程度。可以发现共享时远端访问次数大幅提升,写内存的性能下降非常明显。

问题II —— 并发度变化

并发度提升对应用的性能影响并不统一,有些应用倾向于让任务聚集在少量CPU上以更好的利用热缓存,另一些则倾向于分散任务到不同的CPU来降低延迟,而在共享CPU的场景下,由于存在应用之间的竞争,并发度本身也会时刻变化,应用性能就会随着并发度的变化而出现抖动,也就是俗称的长尾延迟。

问题III —— Cache亲和性

当同一CPU上运行的多个任务,如果他们都操作相同的内存,那么他们就具有Cache亲和性,可以利用热缓存来减少Cache和TLB Miss率。而应用之间共享CPU,单个CPU上就会运行不同应用的任务,他们往往是不具备Cache亲和性的。

通过perf工具可以抓取Cache/TLB的miss/ref次数,比较独享和共享两种配置下,单位时间内的次数差别,就可以确认Cache亲和性的变化。可以发现共享时Cache miss率小幅上升,Cache miss/ref次数大幅上升,同时iTLB miss率亦大幅上升,每次miss都意味着需要内存读取和缓存刷新,其性能影响不言而喻。

解法&不足

K8S

K8S提供了一种解决方案 —— CPU Manager,在部署容器时可以根据CPU拓扑信息,从单个NUMA节点上选取一组未被使用的CPU,通过限定容器使用这些独享的单节点CPU,不仅避免了来自其他容器对于Cache的干扰,又避免了远端内存访问导致的性能下降。事实上,CPU Manager只是把Guaranteed 容器从共享CPU模式回退到了独享模式,这样做仍然存在不足。

首先,虽然解决了跨NUMA的问题,但由于用了独享方式,仍然会存在资源碎片问题,而容器的CPU Request很难保障刚好填满一个NUMA节点,这样就会在各个节点上留下一些无法被分配的零散CPU,进一步扩大了资源浪费问题。

 

其次,CPU Manager在分配CPU后,无法根据各个NUMA节点的负载情况做后续调整,这会导致节点间的负载不均衡。通常各个NUMA节点是拥有各自独立的内存带宽和通道的,如果不能均衡利用这些硬件资源,就会造成部分节点内存带宽吃紧,部分节点浪费的情况,导致容器性能出现抖动。

OS

主流架构都提供了Cache的管控手段,例如Intel RDT,ARM MPAM以及AMD QoS等,然而在容器场景下这些管控手段仍存在问题:

  • 管控对象有数量限制,通常都少于容器数量,只能按容器类别管理
  • 管控方式是限制使用量,容易造成Cache浪费

未来新硬件会提供动态调整限制量的能力,如果能够拓展控制对象的数量匹配容器数量,配合足够灵敏的动态限制就可以更好地解决这一问题,但短期内还无法商用。即便可以,也仅仅是解决了Cache亲和性问题。

如果有一种能够在CPU共享场景下,按应用聚集任务到部分CPU上的功能,就可以在保留碎片利用能力的同时,解决掉这三个问题了。

但目前的主线内核并没有提供这样的机制,需要我们去弥补。Group Balancer (GB)就是其中一种解决方案,这是一种新的负载平衡方式,相对于传统的在各个CPU之间通过迁移任务来平衡,GB则是在各个CPU组之间通过迁移一组任务来平衡,以达到动态的、类CPU绑定的效果。

由于负载均衡本身的弹性特征,GB的动态绑定并不会限制任务的运行范围,而是仅仅增加相关任务的聚集概率,不会存在资源碎片。目前阿里云操作系统团队已经向开源社区推送了RFC补丁,后续版本仍在制作当中,详见附录I。

总结&展望

本文总结了CPU共享相对于独享的一个优势是能够通过利用CPU碎片提升资源使用效能。通过分析共享CPU的特征,我们总结了存在的三个问题:

  • CPU活动范围扩大导致的NUMA跨节点问题
  • CPU活动范围扩大引起并发度变化导致的性能抖动问题
  • 不同应用CPU竞争导致的Cache亲和性下降问题

随后分析并总结了现有的一些解决方案和其缺点:

  • K8S的CPU Manager特性可以NUMA跨节点问题并缓解另两个问题,但会恶化CPU资源使用效能
  • 硬件管控手段可以解决Cache亲和性下降问题,但前提是容器数量比较少,且会造成Cache浪费
  • Group Balancer可以解决这三个问题,还在推送社区的路上

随着CPU共享被越来越多的应用,未来OS必然会进一步增强共享CPU方式下的资源隔离性,Group Balancer可能只是一个开始,我们期待各路大神们各显其能,定型出完整的、通用的解决方案。

附录I -- Group Balancer RFC补丁及测试结果

讨论链接: https://www.spinics.net/lists/kernel/msg4195265.html

Modern platform are growing fast on CPU numbers, multiple
apps sharing one box are very common, they used to have
exclusive cpu setting but nowadays things are changing.

To achieve better utility of CPU resource, multiple apps
are starting to sharing the CPUs. The CPU resources usually
overcommitted since app's workload are undulated.

This introduced problems on performance when share mode vs
exclusive mode, for eg with cgroup A,B and C deployed in
exclusive mode, it will be:

  CPU_X (100%)     CPU_Y (100%)     CPU_Z (50%)
  T_1_CG_A         T_1_CG_B         T_1_CG_C
  T_2_CG_A         T_2_CG_B         T_2_CG_C
  T_3_CG_A         T_3_CG_B
  T_4_CG_A         T_4_CG_B

while the share mode will be:

  CPU_X (100%)     CPU_Y (75%)     CPU_Z (75%)
  T_1_CG_A         T_2_CG_A         T_1_CG_B
  T_2_CG_B         T_3_CG_B         T_2_CG_C
  T_4_CG_B         T_4_CG_A         T_3_CG_A
  T_1_CG_C

As we can see, the confliction between groups on CPU
resources are now happening all over the CPUs.

The testing on sysbench-memory show 30+% drop on share
mode, and redis-benchmark show 10+% drop too, compared
to the exclusive mode.

However, despite of the performance drop, in real world
we still prefer share mode. The undulated workload can
make the exclusive mode so unefficient on CPU utilization,
for eg the next period, when CG_A become 'idle', exclusive
mode will like:

  CPU_X (0%)     CPU_Y (100%)     CPU_Z (50%)
                 T_1_CG_B         T_1_CG_C
                 T_2_CG_B         T_2_CG_C
                 T_3_CG_B
                 T_4_CG_B

while share mode like:

  CPU_X (50%)     CPU_Y (50%)     CPU_Z (50%)
  T_2_CG_B         T_1_CG_C        T_3_CG_B
  T_4_CG_B         T_1_CG_B        T_2_CG_C

The CPU_X is totally wasted in exclusive mode, the resource
efficiency are really poor.

Thus what we need, is a way to ease confliction in share mode,
make groups as exclusive as possible, to gain both performance
and resource efficiency.

The main idea of group balancer is to fulfill this requirement
by balancing groups of tasks among groups of CPUs, consider this
as a dynamic demi-exclusive mode.

Just like balance the task among CPUs, now with GB a user can
put CPU X,Y,Z into three partitions, and balance group A,B,C
into these partition, to make them as exclusive as possible.

The design is very likely to the numa balancing, task trigger
work to settle it's group into a proper partition (minimum
predicted load), then try migrate itself into it. To gradually
settle groups into the most exclusively partition.

How To Use:

To create partition, for example run:
  echo disable > /proc/gb_ctrl
  echo "0-15;16-31;32-47;48-63;" > /proc/gb_ctrl
  echo enable > /proc/gb_ctrl

this will create 4 partitions contain CPUs 0-15,16-31,32-47 and
48-63 separately.

Then enable GB for your cgroup, run
  $CPU_CGROUP_PATH/cpu.gb_period_ms

And you can check:
  $CPU_CGROUP_PATH/cpu.gb_stat

which give output as:
  PART-0 0-15 1008 1086  *
  PART-1 16-31 0 2
  PART-2 32-47 0 0
  PART-3 48-63 0 1024

The partition ID followed by it's CPUs range, load of group, load
of partition and a star mark as preferred.

Testing Results:
  In order to enlarge the differences, we do testing on ARM platform
  with 128 CPUs, create 8 partition according to cluster info.

  Since we pick benchmark which can gain benefit from exclusive mode,
  this is more like a functional testing rather than performance, to
  show that GB help winback the performance.

  Create 8 cgroup each running 'sysbench memory --threads=16 run',
  the output of share mode is:
    events/s (eps):                      4181233.4646
    events/s (eps):                      3548328.2346
    events/s (eps):                      4578816.2412
    events/s (eps):                      4761797.3932
    events/s (eps):                      3486703.0455
    events/s (eps):                      3474920.9803
    events/s (eps):                      3604632.7799
    events/s (eps):                      3149506.7001
  the output of gb mode is:
    events/s (eps):                      5472334.9313
    events/s (eps):                      4085399.1606
    events/s (eps):                      4398122.2170
    events/s (eps):                      6180233.6766
    events/s (eps):                      4299784.2742
    events/s (eps):                      4914813.6847
    events/s (eps):                      3675395.1191
    events/s (eps):                      6767666.6229

  Create 4 cgroup each running redis-server with 16 io threads,
  4 redis-benchmark per each server show average rps as:

                    share mode       gb mode

  PING_INLINE     : 41154.84         42229.27               2.61%
  PING_MBULK      : 43042.07         44907.10               4.33%
  SET             : 34502.00         37374.58               8.33%
  GET             : 41713.47         45257.68               8.50%
  INCR            : 41533.26         44259.31               6.56%
  LPUSH           : 36541.23         39417.84               7.87%
  RPUSH           : 39059.26         42075.32               7.72%
  LPOP            : 36978.73         39903.15               7.91%
  RPOP            : 39553.32         42071.53               6.37%
  SADD            : 40614.30         44693.33              10.04%
  HSET            : 39101.93         42401.16               8.44%
  SPOP            : 42838.90         46560.46               8.69%
  ZADD            : 38346.80         41685.46               8.71%
  ZPOPMIN         : 41952.26         46138.14               9.98%
  LRANGE_100      : 19364.66         20251.56               4.58%
  LRANGE_300      : 9699.57          9935.86                2.44%
  LRANGE_500      : 6291.76          6512.48                3.51%
  LRANGE_600      : 5619.13          5658.31                0.70%
  MSET            : 24432.78         26517.63               8.53% 

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
8月前
|
移动开发 运维 监控
掌握Linux运维利器:查看CPU和内存占用,轻松解决性能问题!
掌握Linux运维利器:查看CPU和内存占用,轻松解决性能问题!
314 0
|
8月前
|
Web App开发 Java 测试技术
ChaosBlade常见问题之演练场景页面乱码cpu使用率图片显示不出来如何解决
ChaosBlade 是一个开源的混沌工程实验工具,旨在通过模拟各种常见的硬件、软件、网络、应用等故障,帮助开发者在测试环境中验证系统的容错和自动恢复能力。以下是关于ChaosBlade的一些常见问题合集:
128 0
|
算法 编译器
【计算机架构】响应时间和吞吐量 | 相对性能 | 计算 CPU 时间 | 指令技术与 CPI | T=CC/CR, CC=IC*CPI
【计算机架构】响应时间和吞吐量 | 相对性能 | 计算 CPU 时间 | 指令技术与 CPI | T=CC/CR, CC=IC*CPI
1248 1
|
1月前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
168 7
|
2月前
|
存储 缓存
CPU性能
【10月更文挑战第30天】CPU性能
140 3
|
3月前
|
存储 缓存 监控
如何提高服务器CPU性能?
如何提高服务器CPU性能?
441 3
|
5月前
|
弹性计算 固态存储 ice
阿里云服务器2核16G、4核32G、8核64G配置不同ECS实例规格收费标准和CPU性能差异
2024年阿里云提供2核16G、4核32G及8核64G等多种服务器配置,用户可根据需求选择不同实例规格如内存型r8i、通用算力型u1等。以华北2(北京)为例,2核16G月费从286.2至385.99元不等;4核32G为572.4至771.97元;8核64G则在1144.8至1543.94元区间。公网带宽与系统盘(如ESSD云盘)亦有多样化选择与价格方案。长期租赁可享折扣,具体价格请访问阿里云官网确认。
191 7
|
4月前
|
编解码 算法 测试技术
CPU性能调节【ChatGPT】
CPU性能调节【ChatGPT】
|
6月前
|
缓存 弹性计算 数据库
阿里云2核4G服务器支持多少人在线?程序效率、并发数、内存CPU性能、公网带宽多因素
2核4G云服务器支持的在线人数取决于多种因素:应用效率、并发数、内存、CPU、带宽、数据库性能、缓存策略、CDN和OSS使用,以及用户行为和系统优化。阿里云的ECS u1实例2核4G配置,适合轻量级应用,实际并发量需结合具体业务测试。
122 0
阿里云2核4G服务器支持多少人在线?程序效率、并发数、内存CPU性能、公网带宽多因素
|
7月前
|
弹性计算 安全 前端开发
阿里云服务器ECS通用型、计算型和内存实例区别、CPU型号、性能参数表
阿里云ECS实例有计算型(c)、通用型(g)和内存型(r)系列,区别在于CPU内存比。计算型1:2,如2核4G;通用型1:4,如2核8G;内存型1:8,如2核16G。实例有第五代至第八代,如c7、g5、r8a等,每代CPU型号和主频提升。例如,c7使用Intel Ice Lake,g7支持虚拟化Enclave。实例性能参数包括网络带宽、收发包能力、IOPS等,适合不同场景,如视频处理、游戏、数据库等
229 0

热门文章

最新文章