Curve ChunkServer的CPU瓶颈问题
Curve是网易数帆开源的新一代分布式存储系统,具有高性能、高可用、高可靠的特点,可作为多种存储场景的底层存储,包括块存储、对象存储、云原生数据库、EC等。
对于分布式块存储系统来说,IOPS是最重要的一个性能指标。从Curve目前的性能测试情况看,读IOPS瓶颈在Client端——对于6个存储节点的集群,单个Client节点读IOPS接近30万,两个Client节点读IOPS接近60万。而Curve的写IOPS还有一定提升空间——对于6个存储节点的集群,IOPS只能达到26万~28万,而ChunkServer节点CPU使用率接近100%,而底层SSD的使用率则不到90%。因此,随机写IOPS场景是Curve的一个优化重点。
在测试环境A 中部署Curve(具体配置见附录1),在Client节点创建10个卷,进行4KB随机写测试。结果显示,写IOPS约为13.5万,而此时ChunkServer节点的CPU使用率接近100%,而所有SSD的使用率平均不到85%。
这表明,ChunkServer端CPU成为性能瓶颈。考虑到目前测试环境的SSD配置较低,若使用高性能NVME SSD,其IOPS可能比现有SSD高一个数量级,届时CPU性能瓶颈将更为严重。因此,优化CPU性能,释放SSD的I/O能力,是Curve性能优化的一个重要方向。本文围绕Curve的CPU性能优化进行了一些探索和实践。
CPU性能优化方法
在进行CPU性能优化之前,我们必须进行性能测量,对CPU性能进行量化分析,从而有的放矢,针对性优化。
先回顾下CPU的基本工作原理:现代CPU都是多核心架构,软件需要以进程或线程的方式在CPU核心上运行。操作系统会将每个CPU核心的运行时间划分为毫秒级的时间片,然后通过调度算法为每个时间片选择一个就绪的线程执行。时间片结束或者运行的线程需要等待资源(I/O, 锁,条件变量等),操作系统就会进行上下文切换,将对应的CPU核心分配给另一个就绪的线程。
因此,我们可以从线程和CPU两种视角去测量CPU性能。
- 从线程视角,我们可以测量软件线程的CPU时间开销,定位CPU时间开销较大的模块或函数,进行重点优化,这也是一种常用的定位方法;另外,还可以测量线程等待锁的时间,判断是否有不合理的锁竞争影响软件运行效率;而线程等待I/O、条件变量等资源的情况,一般可以从设计和代码层面进行梳理分析,本文不再赘述。
- 从CPU视角,我们可以从外部测量CPU使用率和上下文切换频率,判断CPU时间是否被充分利用(这方面的知识大家都耳熟能详,而Curve在这方面也没有明显问题,因此本文不再赘述);还可以从内部测量CPU微指令运行效率,分析CPU运算单元是否被充分利用,这需要采集CPU内置PMU(Performance Monitoring Unit,性能监控单元)的监控数据,并使用专门的分析方法,从而定位CPU运行的瓶颈。
下文将从这两个角度,分别对Curve的CPU性能进行测量和优化。
Curve的线程级性能分析
线程级CPU性能分析可以从三个方面进行:
- 自上而下的CPU时间分析,即按照软件调用关系,从主函数到各子函数,一层层分解和剖析CPU时间。这样可以大体区分各模块、函数的CPU时间占比,从而确定CPU时间开销最大或者超出预期的模块或函数,为下一步深入分析提供参考。
- 自下而上的CPU时间分析,即按照函数的被调用关系,从最底层子函数向上,一层层追溯各调用分支的CPU时间占比。这种方式可以定位出被多个父函数调用,其总运行时间异常大的函数。
- 线程等待时间分析,上面两方面都是关注的线程在CPU上运行的时间,属于on-CPU分析;线程等待时间分析则关注线程因为等待资源而没有在CPU上运行的时间,属于off-CPU分析。线程可能因为I/O、条件变量等资源未就绪而等待,也可能因为资源竞争(比如锁)而等待,前者一般属于正常情况,而后者需要重点关注。
下面在测试环境B(见附录2)进行4KB随机写测试,然后分别从这三个方面对curve进行CPU性能分析实践。
软件配置如下:
代码版本:d29c4991 (略新于v1.1-beta)
chunkserver数量:每节点20个chunkserver
copyset数量:4000个(测试前已确保leader均衡,range <= 2)
用户卷数量:10个
用户卷大小:30GB
4k随机写IOPS:234k
自上而下的CPU时间分析
使用vtune采集ChunkServer进程的threading数据:
vtune -c threading -target-pid=pidof curve-chunkserver | awk '{print $1}' -duration 50
结果显示:
- braft日志落盘部分的CPU时间占比最大,超过23%,最值得关注;
- brpc模块的CPU时间占比17.8%;
- bthread的work_thread函数CPU时间占比11.3%,偏高;
- ChunkServer并发控制层(大部分为写数据开销)的CPU时间占比11.2%;
- bvar的sampling_thread函数的CPU时间占比4%,超出预期。
小结:
- braft日志落盘与并发控制层落盘的CPU开销达到整个进程的近三分之一,考虑后续通过异步改造等方式进行优化;
- bthread个别函数的CPU时间偏大,需要关注;
- bvar个别函数的CPU占用比较异常,需要定位。
自下而上的CPU时间分析
对上述测试数据,使用vtune进行自下而上的CPU分析,其测量结果如下所示:
可以得到以下信息:
- ChunkServer自身代码耗时137秒,除此之外,其调用的外部lib库共耗时约99秒。
- bthread模块有6个函数的CPU时间开销名列前茅,累计有44秒,占比很大,需要特别关注。
- 另外,bvar模块有6个函数的CPU时间上榜,累计有22秒,对于一个维护监控指标的工具,这个开销是过大了,需要定位分析。
对于bthread CPU开销较大的问题,Curve团队从其它方面进行了测试优化,写IOPS最多可提升约20%,具体情况就不在这里展开讨论了。
对于bvar模块CPU开销过大的问题,我们进行了测试分析。在代码中去除braft、brpc模块中几个耗时较长的bvar操作语句后,IOPS从236k提升到246k,提升了4%。
可见bvar确实对性能产生了影响,但我们对bvar监控指标的预期是性能下降低于5%,这样的影响可以接受。因此,我们暂不对bvar部分进行优化改动,但会对bvar指标的使用进行规范,包括增加监控开关,去除多余的监控指标,及控制新指标的增加等。
线程等锁时间分析
因vtune多次测试未采集到线程wait time相关数据,我们使用brpc内置的contention profiler组件,分析花在等待锁上的时间及发生等待的函数[1]。
在测试环境A中(见附录1)进行随机写测试,同时打开brpc服务的web页面,点击contention选项卡,触发contention profiler。(为确保测试结果稳定,总测试时间被修改为30s)
测量结果如下所示:
在30秒的采集时间内,ChunkServer等待锁的总时间为0.442秒,比例很小。
其中,curve::common::TaskQueue::Push等待锁的时间占比**86.8%,**这也符合并发控制层的逻辑。
因此,ChunkServer进程在等待锁方面很正常,不需要关注。
CPU微指令运行效率的测量方法TMAM
本节主要介绍CPU微指令运行效率的测量方法。首先,简单介绍下CPU的微指令和流水线的概念。操作系统提交给CPU的指令,会被CPU分解为若干条微指令(uOp),并在内部以流水线的方式调度执行。一般来说,CPU 流水线在概念上分为两部分,即前端(Front-end)和后端(Back-end)。Front-end 负责获取程序代码指令,并将其解码为一个或多个称为微操作(uOps)的底层硬件指令,并发送到后端。Back-end 负责监控 uOp 的数据何时可用,并在可用的执行单元中执行 uOp。 uOp 执行的完成称为退役(Retirement),uOp 的执行结果提交并反馈到架构状态(CPU 寄存器或写回内存)。
我们可以用流水线槽(pipeline slot)代表处理一个 uOp 所需的硬件资源。在最近的英特尔微体系结构上,流水线的 Front-end 每个 CPU 周期可以分配4个 uOps ,而 Back-end 可以在每个周期中退役4个 uOps。在每个 CPU 周期中,pipeline slot 可以是空的或者被 uOp 填充。 如果在一个 CPU 周期内某个 pipeline slot 是空的,称之为一次停顿(stall)。如果 CPU 经常停顿,系统性能肯定是受到影响的,如下图所示(绿色圆圈表示该流水线槽有微指令运行,灰色圆圈表示没有):
由此可见,我们可以统计流水线槽的运行状态,以此衡量CPU微指令运行效率。
现代 CPU 大多具有性能监控单元(Performance Monitoring Unit, PMU),用于统计系统中发生的特定硬件事件,例如缓存未命中(Cache Miss)或者分支预测错误(Branch Misprediction)等。同时,多个事件可以结合计算出一些高级指标,例如每指令周期数(CPI),缓存命中率等。一个特定的微体系架构可以通过 PMU 提供数百个事件。对于发现和解决特定的性能问题,我们很难从这数百个事件中挑选出那些真正有用的事件。 这需要我们深入了解微体系架构的设计和 PMU 规范,才能从原始事件数据中获取有用的信息。
Intel公司提出了自顶向下的微体系架构分析方法(Top-Down Microarchitecture Analysis Method, TMAM),可以在乱序执行的内核中识别性能瓶颈。TMAM方法通过CPU内置的PMU数据,测量这些流水线槽的运行状态,从而确定cpu微指令运行效率的瓶颈,显示运行应用程序时 CPU 流水线的使用情况。(CPU微指令与TMAM相关背景内容主要摘自[2]和[3])
TMAM方法将CPU性能问题划分为4大类——FrontEnd Bound, BackEnd Bound,Retiring, Bad Speculation。每大类又可划分为1-3个层级的若干子类,我们只需利用相关工具按图索骥,即可一层层区分、细化cpu性能问题,直至最终确定影响最大的问题所在。最终目标是,将Retiring比例提升至最大。
这种自顶向下的分析框架的优点是一种结构化的方法,有选择地探索可能的性能瓶颈区域。 带有权重的层次化节点,使得我们能够将分析的重点放在确实重要的问题上,同时无视那些不重要的问题。例如,如果应用程序性能受到指令提取问题的严重影响, TMAM 将它分类为 Front-end Bound 这个大类。 用户或者工具可以向下探索并仅聚焦在 Front-end Bound 这个分类上,直到找到导致应用程序性能瓶颈的直接原因或一类原因。TMAM方法的具体问题分类及对应的优化方法可查询intel各cpu的优化指南(如Xeon E5 v3系列cpu的优化文档为[4])
Intel VTune Profiler是一款全面的性能测量分析软件[5],它可以从系统、线程、CPU微架构等多个角度测量和分析性能数据,其中CPU微架构部分就应用了TMAM方法。
TMAM在Curve的实践
在测试环境中,对curve进行4KB随机写,软件配置如下:
代码版本:d29c4991 (略新于v1.1-beta)
chunkserver数量:3个节点,每节点20个chunkserver
copyset数量:500个(测试前已确保leader均衡,range <= 2)
用户卷数量:10个
用户卷大小:30GB
4k随机写IOPS:129k
同时使用Intel VTune Profiler采集CPU微架构相关性能数据,结果如下图所示:
从上图中vtune的cpu微架构性能数据看,主要的cpu瓶颈在Frond-End Latency, 占比50.3%。参考intel的优化指南,可以实施的优化方法主要是编译优化,包括:
- PGO (profile-guided optimization) 编译优化
- -O3编译优化性能
- -Os/-O1优化代码尺寸。
- LTO链接时优化。
- march/mtune, 针对cpu指令集优化。
其中,PGO优化是指基于性能反馈的编译优化,它需要进行两轮编译[6]。第一轮编译完成后,会生成带profiler功能的可执行文件,将它部署到测试环境,按最常用的负载进行运行后,它会自动采集性能数据;然后将性能数据复制到编译环境中进行第二轮编译,即可得到优化后的可执行文件。
对curve分别应用这些优化方法,实测方法3相比默认的-O2优化,性能明显下降;而方法4的优化会导致编译失败,未能解决。因此,curve无法应用这两种优化。
实测单独进行-O3优化或PGO优化时,性能提升在-5%和5%之间,无提升。
最终结合-O3和PGO优化,及cpu指令集优化时,性能提升了7.7%(IOPS从129k提高到了139k)。
具体的编译优化选项为:
第一遍编译时gcc优化选项:
-O3 -march=core2 -mtune=haswell -fprofile-generate=/tmp/pgo
链接优化选项为-fprofile-generate=/tmp/pgo
第二遍编译时gcc优化选项:
-O3 -march=core2 -mtune=haswell -fprofile-use=/tmp/pgo -fprofile-correction
链接优化选项为-fprofile-use=/tmp/pgo
从vtune的微架构性能数据看,编译优化后cpu的pipeline slots的retiring比例从19.1%提升到了22.3%。
而Front-End Bound从58.7%降至47.3%,这证明编译优化确实缓解了Front-End Bound,提升了CPU执行效率。
总结
本文从线程的CPU运行时间与锁等待时间,以及CPU微指令运行效率等多个角度,对Curve ChunkServer的CPU瓶颈问题进行了测量分析和优化实践,结果如下:
- 从线程的CPU运行时间方面看,braft日志落盘与并发控制层落盘的CPU开销达到整个进程的近三分之一,考虑后续通过异步改造等方式进行优化;bthread组件CPU占比过高的问题,通过其它方法进行了优化,效果显著;bvar对性能影响不到5%,可以接受,后续将对bvar使用进行梳理和限制。
- 从线程的等锁时间看,ChunkServer主要是并发控制层有少量等锁情况,属于正常状态。
- 在CPU微指令运行效率方面,本文通过自顶向下的TMAM方法进行了分析,确定ChunkServer主要问题是Front-End Bound,并通过编译优化方法进行了优化,使IOPS提升了7.7%。
另外,Curve近期还有其它优化工作在进行,欢迎关注下一个版本v1.2,其性能会进一步显著提升。
附录1 测试环境A配置
client节点(1台)
双路Intel(R) Xeon(R) CPU E5-2680 v4 @ 2.40GHz
256GB内存
双10Gb网卡bond
mds和chunkserver节点(3台)
双路Intel(R) Xeon(R) CPU E5-2670 v3 @ 2.30GHz
256GB内存
双10Gb网卡bond
20个 SSD用于chunkserver服务(SATA3接口,型号分别为INTEL DC S3610和三星PM863a,标称写IOPS分别为27000和24000)
copyset总数量为500个
附录2 测试环境B配置
client节点(1台)
双路Intel(R) Xeon(R) CPU E5-2660 v4 @ 2.00GHz
256GB内存
双10Gb网卡bond
chunkserver节点(6台,其中3台部署了mds)
双路Intel(R) Xeon(R) CPU E5-2670 v3 @ 2.30GHz
384GB内存
双10Gb网卡bond
20个 SSD用于chunkserver服务(SATA3接口,型号是INTEL DC S3610标称写IOPS为27000)
copyset总数量为500个
作者简介
秦亦,网易数帆资深服务端开发工程师,计算机系统结构博士,专注于分布式存储领域;曾参与华为FusionStorage分支项目,独立攻关天玑数据PhegData X数据存储云平台的后端存储引擎,现为Curve团队核心开发。
参考
[1] https://github.com/apache/incubator-brpc/blob/master/docs/cn/contention_profiler.md
[3] https://kernel.taobao.org/2019/03/Top-down-Microarchitecture-Analysis-Method/
[6] https://en.wikipedia.org/wiki/Profile-guided_optimization
相关链接
来源:oschina
链接:https://my.oschina.net/u/4565392/blog/4807377