写在前面

      大约4年前在中科院软件所,为评估国产兆芯CPU性能,我接触到一些benchmark,涉及CPU指标的就有ubench、sysbench、c-ray、SPEC和unixbench等。

       工具在手,不要动脑,跑起来就行。后来情况变了,每个月都有数个系统性能调优的kpi压在身上,你不光要知道这些性能怎么测,还得分析瓶颈在哪,最后还要不断实验去提升性能。

       能力有限,很多就不会去关注,像sysbench也是个老掉牙的benchmark了,诞生到现在有15年历史,一个工具走到现在,还有它使用的场景,那肯定有它的价值。

       正巧最近又需要验证虚拟化下CPU的性能,仍然利用sysbench去测试,这次既然测了,就不单单只看跑分,看看数据背后的意义。       

       整篇文章分以下几小章节:

          一  sysbench测试CPU的过程

   二  计算素数对CPU性能的参考价值

   三  如何评估测试数据

          四  总结


一 sysbench测试CPU的过程

       使用benchmark前,不妨先看下readme,sysbench在CPU上是这样自我定义的:

      'cpu': a simple CPU benchmark

       划重点:simple。工具设计之初,就不是为CPU考虑的,它最常使用场景基本在mysql等数据库评测上。所以自评为simple benchmark,必然有原因,这个后面详说。

        回归正题,sysbench在大多数Linux下建议yum或apt-get安装,万一需要手动编译,需要注意2点:

l  如果你的sysbench是运行在64位CPU上,请先确保编译的是64位二进制文件;

l  万一你的环境是32位CPU,要知道在32位CPU上进行64位操作数的运算,需要使用多条32位的汇编指令来模拟其行为,随之带来的性能开销势必影响到结果。


(1)一般测试方法

        事先声明,我用的是sysbench1.0.17版本,自从1.0.8版本后,一改以往使用计算达到10000内素数所用时间为测试结果, 增加了events per second为输出报告。

        测试方法大同小异,sysbench + 参数 + 测试项 + 命令,如下图:

           image.png        

        以CPU为例(测试在4线程下CPU的运算速度):

           image.png

        在本例测试中,只是采用最小化参数:--threads,其他常用参数还包括:

         测试10秒钟,分析数小时,CPU性能只看跑分可不够_第1张图片


        根据实际需要,可选择多种参数组合,本文只采用线程作为参数,测试结果如下所示:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第2张图片

       测试输出清晰,数据一目了然,benchmark轻度用户重点关注2个数据就好,一是Prime numbers limit,二是events per second。

        以上面为例,最终结果就是计算10000个素数,4线程平均每秒完成event个数为549.36,数值越大代表性能越好。

        如果只简单衡量云主机CPU性能,以上2个结果足够了,明白了在什么样的基数下测试,并且知道每秒完成的events,可以获得1个基准数据。


(2)sysbench的实现方式

        工具run了起来,数据也拿到了,但是离马放南山还早。这时候你还不知道数据准确与否,如果是准确的,是不是你想要的最优结果。

        在评估数据之前,先了解下sysbench是怎么去测试CPU的,做到知己知彼后,再去看测试结果,说不定会有更多体会,特别是把测试中观察到的性能问题跟系统原理关联起来。

        sysbench作为一个多用途开源benchmark,按照框架设计惯例,其CPU、thread、io、memory和数据库也遵循着松耦合的设计,CPU的源码有独立的模块。


  •   简约不简单的cpu module

            采用农村包围城市,先从CPU测试源码入手,源码见/src/tests/cpu/sb_cpu.c

            测试10秒钟,分析数小时,CPU性能只看跑分可不够_第3张图片

           图1:sb_cpu.c 头文件声明

            你不熟悉sysbench的代码结构也没关系,仅从sb_cpu.c中简单几行头文件声明,就可以得到很多信息,抛开前面2个config.h、sb_win.h不谈(测试环境在Linux上),真正用到的就2个类库路径里面的头文件:和1个内部实现函数声明的"sysbench.h"。

  测试10秒钟,分析数小时,CPU性能只看跑分可不够_第4张图片


           重点关注"sysbench.h",但具体实现还要看sysbench.c,暂且不表,继续看/src/tests/cpu/sb_cpu.c         

           测试10秒钟,分析数小时,CPU性能只看跑分可不够_第5张图片

                                                                                                                          图2:初始化的CPU测试参数

             图2中,定义了一个cpu默认参数,如果不特别指定,cpu-max-prime默认以10000作为上限。这里工具采用static函数声明的方式,直接引用/src/sb_options.h中的sb_arg_t结构体,简单声明测试设置的name、desc、value和type,在以后的sysbench.c会多次调用,如图3所示:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第6张图片

图3:sb_arg_t结构体

             沿着/src/tests/cpu/sb_cpu.c代码继续看,是对CPU测试的函数声明和静态变量定义,在cpu_test中通过变量可看到CPU性能测试流程,如下图4中:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第7张图片

图4:cpu_test静态变量的定义

              图4中,基于sb_test_t的结构体变量cpu_test,ops下所有变量在sb_test_t中引用了sb_operations_t。cpu_test完全依据sb_test_t结构体中包含的数据项,从名称设置开始,到初始化操作,再到定义核心event函数执行、报告输出,最后到结束后的动作。短短10几行,就把CPU测试场景描述清楚了,剩下要做的只是代码依次执行罢了。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第8张图片

图5:cpu-max-prime取值判断

               

              图5中,cpu-max-prime在/src/tests/cpu/sb_cpu.c代码中有单独实现,其他类似time、event、threads的实现全都放在sysbench.c中。

              sb_get_value_int()函数在sb_options.c中已完成定义,很多参数都要sb_get_value_int去获取。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第9张图片

图6:CPU测试的主要执行环节

                cpu_execute_event是sysbench在CPU性能评估最主要环节,之前代码多次提到prime字眼,知道是素数,通过百度也知道它的计算原理,但在cpu_execute_event函数中详细定义了具体计算方式。在这种模式下,每个请求都由素数计算组成,由cpu-max-primes选项指定的值为上限,所有的计算使用64位整数执行。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第10张图片

图7:CPU报告实现

                 图7中,cpu_report_cumulative是最后的实现环节,采集、统计及打印是一连串的处理过程,如果要实时打印report,要停止运行计时器。具体代码实现在sysbench.c中sb_report_cumulative(),这是个复杂的处理流程。


  •   管中窥豹看框架

            sysbench测试CPU的代码没那么复杂,素数原理摆在明面,算法固定,自身实现代码比较清晰,单看素数计算的部分,任何语言都能实现,自己写个脚本都能直接用,难点主要在events和cumulative处理上,这也是基准工具的魅力所在。

            前文对CPU模块有了粗略分析,这里我用一张图将整个流程贯穿起来,如下图所示:

           测试10秒钟,分析数小时,CPU性能只看跑分可不够_第11张图片

    图8:CPU TEST 关系图

           

          图8中,除了能看到代码之间的调用链,有一点值得注意,就是几个struct:sb_test_t、sb_operations_t,前文提过,基于sb_test_t的结构体变量cpu_test,ops下所有变量在sb_test_t中引用了sb_operations_t。

           阿里云性能专家西邪在他的《sysbench的框架实现介绍》文章中提到一段话:

                    sysbench是一个总体框架,它用来操作各个测性能的计算,那各个部门只需要做的一件事情是声明需要的实现。只要理解三个struct就可以了    

             文中所提到的三个struct,就是指sysbench.h中的sb_test_t、sb_operations_t和sb_builtin_cmds_t,见下图9:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第12张图片

图9:sysbench case的主要结构体


             从CPU入手看sysbench的实现,3个struct定框架,实现了从测试场景结构到操作结构,再到命令实现结构。

              写到这里,对sysbench的框架在本文只是粗略了解,有些地方并未深入,比如thread的设计。


二 计算素数对CPU性能的参考价值

       一直觉得数据必须要产生价值,价值就是对外能够给用户带来实际意义,比如帮助用户虚拟机选型,对内能够推动研发不断优化提升用户体验等。

       有一段时间,因工作需要,我验证了很多操作系统的CPU性能,跑了很多次sysbench,最快只要10秒钟出结果,立等可取。但是跑分不是比大小的game,测的越多越迷茫,甚至都怀疑基准测试是不是认真的,进而对数据打上问号,这样得出的数据有没有参考价值?

       sysbench去评估cpu性能,打个比方就像田径赛事,对cpu的运算测试,包括素数计算,整数,还是浮点数,好比100米、110米栏等比赛,赛道是测试对象,而裁判就是benchmark,都是看谁跑的快。

(1)为啥要使用计算素数的方式

       在前文第一章节讲sysbench readme时说过,评估CPU性能,sysbench只是一个“a simple CPU benchmark",再结合/src/tests/cpu/sb_cpu.c源码,可清晰看到它就是通过执行64位整数相加、整除等操作来获取素数,直到满足设定最大值,这个过程用来评估CPU运算速度。

       所谓素数就是质数,一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数。

       为啥要采用素数相加计算来判断cpu性能?这个可追溯不到历史,也许当年工具开发者看了电视节目,节目正好讲什么人机大战、数学猜想等,诸如此类,who care?

(2)素数运算衡量的是CPU什么能力?

       在讲CPU源码时,提到/src/tests/cpu/sb_cpu.c中cpu_execute_event函数,如下源码所示:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第13张图片

       

        利用计算最大素数来衡量CPU运算速度,在代码中主要使用到的是整数计算(加法),cpu整数能力就是cpu执行加法指令的能力,cpu一秒能执行多少条加法指令,整数运算是cpu最基本能力。

        通过sysbench得到的CPU事务数,能一定程度上反映CPU整数运算性能,但不完整(只涉及加法),相加是衡量CPU整数能力,但是相除就是浮点运算能力(但涉及不多),实际上日常大多数应用(包括sysbench)都是整数计算为主的程序(会包含少量浮点指令)。

        总结一下,利用sysbench计算素数获取CPU运算速度,比较接近实际应用CPU场景(不包括GPU),具有代表性。如果想独立评估CPU整数或浮点运算能力,建议尝试别的工具。


三 如何评估测试数据

        到了现在,sysbench测试方法熟悉了,结果也有了,把数据填进excel中呈交上去,来杯咖啡可以悠闲的等待下班开黑打野了。不过还没开心多久,同事就来问你:


测试10秒钟,分析数小时,CPU性能只看跑分可不够_第14张图片

       你不得不重新打开电脑,运行工具,试图从数字中找到其中的奥秘,可是很快就发现除了从sysbench获取一组孤零零数字外,空荡荡的脑子里好像啥也没有留下。是不是白算了?并没有,CPU不会白算,每一步都算数,测试结果只是空洞的一串数字,还需要人为赋予价值。

(1)磨刀不误砍柴工    

       无论是基准测试,还是性能测试,在执行过程中有一个很容易做到,但很多人都忽略的现象:不会做笔记。不是不会做,是压根不记录过程中任何输出,没有这些记录,会给后续跟踪性能问题带来麻烦,产生问题原因从手指间溜走,时间浪费不说,还容易带给你负能量。

       我个人使用OneNote收集过程, 文字+图片任意组合,方便日后追踪,如果遭遇环境重装、时间不允许等客观因素不至于老鼠拉龟,无从下手了。

(2)给测试定基调

       我的同事老王说每次发布新版,都要对CPU和内存运算能力重新验证,好像没意义。

       狭义上的“意义”是啥?意义=价值,就是投入时间后所得到的回报。说到价值,其实在第2章节中分析素数运算衡量CPU什么能力时已给出了答案,《学霸的黑科技系统》一书在某个章节中也曾提到“梅森素数”,你说梅森素数有什么用?好像也举不出什么实际应用的例子,通常它都是被用作考验计算机性能,IntelSkylake芯片也曾由此发现bug。

       不能指望每次基准测试都能发现bug,那这个性能评估还要不要做了?在回答问题之前,先要明白基准测试的目的。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第15张图片


       总结一下:

n  基准测试一般发生在环境变更、版本迭代、配置或参数改变的前后;

n  当为软件或系统创建性能基准后,这个数据可作为参照,用来判断任意项变更给系统带来的影响;

n  了解系统优化前后的性能提升/下降指标,获取系统整体性能趋势,及早识别系统性能风险


 这么看来,我们的工作还是有做的必要性,但不妨定3个小目标:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第16张图片


(3)从sysbench中读懂数据

        sysbench数据模板是固定的,测试结束会将数据直接嵌套进去再呈现到终端。拿到数据先做2个确认:

       →确认1:Number of threads 和你发起测试参数中一致

       →确认2:Prime默认为10000,除非你发起测试参数中有特别指定值

        步骤正确,参数没错,我们从上到下依次看下sysbench输出的测试数据。

        测试结果中最重要的就是CPU speed,即:所有线程每秒完成的events数,这个数值越大代表性能越好,如下图10:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第17张图片

图10:cpu speed结果

  每秒完成的events数可以理解成跑分,在手机领域每当手机厂商发行新手机产品,各种发布会上毫不例外的都会附上跑分。但是单看跑分还不够,还要重视延时。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第18张图片

图11:Latency结果

   上图11中, sysbench给出了Latency的结果,包括min、avg、max和95%时延,类似jmeter输出,这个数值越小代表性能越好。在分布式存储(如ceph)领域,很多指标都将延时作为重要参考,但在计算能力上,对CPU运算延时重视度并不够,由于CPU和内存、硬盘不公平的进化规律,很多场景下CPU可能会消耗在IO等待中。

(4)不同工具齐上阵,寻找蛛丝马迹

       众所周知在Linux上,应用是不能直接访问底层硬件,当进程需要访问硬件时,必须由用户态模式切换至内核态(暂不考虑KVM虚拟化,在虚拟世界还要借助intel硬件虚拟化辅助),再通过系统调用访问硬件。so,在Linux中有一个放之四海皆准的应用负载模式,如下图12所示:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第19张图片

图12:linux应用负载模式


       这里把sysbench当成一个普通应用,和其他同样运行在虚拟机linux系统上的程序没什么两样,当它run起来后,会发生什么?

       任何应用进程,哪怕复杂度很低,也是依赖Linux库来执行操作,sysbench测试CPU中一定需调用os库函数,通过ltrace可以跟踪进程调用库函数的情况,如下图13所示:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第20张图片

图13:ltrace库函数调用情况

        库函数调用开销是一个可能存在性能瓶颈的地方,这就要看进程在需要的库中花费的时间开销了,先从调用哪些库函数入手,看看他们分别做了什么。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第21张图片

图14:sysbench库函数调用矩阵


l  clock_gettime贯穿整个sysbench过程,在库函数调用中占了半数,调用了65563次,开销时间随着sysbench 的--time参数改变而改变。有一点值得注意的是,sysbench进程已结束,但clock_gettime仍持续长达10秒+调用;

l  log、floor函数调用一直维持在17%左右,这是评估CPU运算所使用的算法核心函数,从时间开销来看,此处并未发生明显瓶颈;

l  futex是对内核的调用,主要就是管理进程挂起时的等待队列以及锁的睡眠与唤醒操作(即futex_wait、futex_wake)


    ok,通过ltrace定位到进程具体调用库函数,并且也知道哪些函数调用次数最多,下一步就是要在代码中找出clock_gettime在哪里被调用。我们利用sysbench执行测试,自然要在sysbench源码中去寻找。下图15是clock_gettime在代码中的调用位置,只是简单列出,不包含调用链。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第22张图片

图15:sysbench代码中clock_gettime调用位置


            同样log、floor在sysbench代码也有相应调用关系,在本文不再详述。

            至此,毋庸置疑的是clock_gettime在sysbench调用过于频繁,这是跟sysbench的架构设计有关系,如果通过修改源码让其少调用库函数的次数,或者尝试修改库的源代码,是不是能够再提高性能?

            醒醒吧,性能真有那么简单就下结论?首先修改库源代码并不实际,牵一发而动全身;其次库函数调用频繁并不代表此处遭遇瓶颈,需要和资源开销综合观测。


     1)CPU使用率和平均负载

        CPU使用率人人都会查,这是首先能想到CPU监控指标,下图16是sysbench执行中CPU实时监控情况,可以看到4线程同时在跑,平均分配到4个vCPU上,都满负荷运行,过去1分钟的平均负载达到4.06,并且持续在这负载线上。

        结论1:平均负载最理想的情况是等于CPU 个数,4.06代表系统并未发生过载现象

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第23张图片

图16:CPU使用率


     2)用户/内核消耗CPU的时间,有无io等待

         下图17是实时top监控,us100%,内核无CPU消耗,至始至终CPU没有出现iowait状态,内存也无开销,这是一个CPU基准测试工具该有的表现,只针对CPU检测,不牵连无辜。

         结论2:CPU占用时间片正常,无等待I/O

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第24张图片

图17:us/sy/wa的监控

      3)系统、线程上下文切换

         图18是vmstat实时监控,看到随着sysbench的运行,in次数增加明显,上下文切换短时增加,后趋于减小到正常水准。

         结论3:sysbench运行中,中断次数明显增加,但不能确定是软中断,还是硬中断;系统上下文切换无异常

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第25张图片

图18:系统上下文切换/中断次数

        图19是利用pidstat监测到的线程开销,可看到4个sysbench线程均存在非自愿的上下文切换,次数维持在5-20之间,上下文切换必然产生性能开销,但如果超过1万或者切换次数出现很大增长,就可能出现性能问题。

        结论4:sysbench在多线程运行下会存在非自愿的上下文切换,在单线程下则不会发生。整体上下文切换稳定在个位数,不会对性能结果造成明显影响。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第26张图片

图19:线程上下文切换

      4)软硬件中断

         之前vmstat监控到中断次数明显增加的情形,但无法判断中断类型,用mpstat继续排查后,发现4个vcpu并没有明显的中断发生,如下图20中%irq、%soft所示:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第27张图片

图20:mpstat监控CPU中断         

        先排除掉sysbench运行时性能瓶颈导致的中断增加,再监控下硬中断/proc/interrupts信息,找到中断发生的地方。图21中,通过测试前后对比,找到硬中断产生的位置,都是由LOC产生的Local timer interrupts,LOC是LInxu使用的驱动,用户无法访问和配置,这些都属于时间中断。

        结论5:Local timer interrupts的发生,与频繁调用clock_gettime有关

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第28张图片

图21:interrupts实时信息

(5)学会对比

       从资源视角转了一圈,依次分析了load average、上下文切换、CPU使用、io等待和软硬件中断等,除了clock_gettime现象,其他并没发现明显性能瓶颈的位置,掉转船头,再回到sysbench调用库函数问题上。

       ltrace是跟踪系统函数调用好工具,不过精准力度欠缺了些,下面使用perf看一看sysbench在运行中的热点函数是哪些。

       在图22中,perf top实时看到当前系统上所有函数执行情况,不过什么鬼,在虚拟机中perf居然未能定位本地符号表对应的symbol和地址对应关系,有些符号不能解析为函数名,只能用地址表示,这谁看的懂。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第29张图片

图22:虚拟机上perf追踪各级函数执行情况

        不能解析函数名,猜测是KVM虚拟化问题,找一个物理机用同样测试参数试下,可以看到sysbench 100%执行的是哪个函数,如图23所示:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第30张图片

图23:物理机上perf实时追踪

       cpu_execute_event是sysbench评估最主要实现,在前文代码解读中已详细讲到。为了得到更细粒度分析,还可执行annotate,在C和汇编混合显示下获取热点函数下cpu指令,如图24所示:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第31张图片

图24:annotate获取更详细的热点指令

       100%时间开销在执行cpu_execute_event操作上,分析运行代码,其中72.08%的时间占用%rcx一项指令上,这属于64位汇编参数传递的知识范围。

       我曾经遇到一个奇怪现象,在64位linux系统上,素数运算速度反而低于32位,通过查找指令延迟表发现64位整数除法指令在某国产CPU上的执行时间远长于32位执行时间。这就解释了64位操作数编译成64位可执行程序为什么会发生性能异常。

       后来我在cenots 7.2 64位系统上做过对比实验,确定相同的处理器下,64位系统素数运算速度是高于32位的,CPU64位指令集可以运行64位数据指令,一次可以提取64位数据,比32位提升一倍。

       在64位系统中,增加了比32位系统多的寄存器。当sysbench调用函数时,传递参数会发生改变。一般参数都放在寄存器中传递,回过头看下sysbench在参数传递的代码,可以看到一个函数在调用时,前四个整型值依次传给寄存器 rbx、rax、rcx和rdx。

       因为涉及知识盲区,这里不好评价是否存在问题,底层约定的通用参数传递方式,硬件层是无法改变,sysbench代码倒是可以变动,但效果未知。

       如果从主动寻找性能问题无果,可以试试被动对比。所谓被动,是通过对比方式找到可能存在的问题。


     1)不同物理CPU的性能表现

        手上正好有2套不同CPU物理环境,试下物理cpu主频、核心进程的不同对性能的影响程度。

         测试10秒钟,分析数小时,CPU性能只看跑分可不够_第32张图片

图25::不同物理CPU下的性能对比


       从图25折线图看到,在Intel Xeon E5-2680 V4上执行sysbench基准测试得到的数据优于Intel Xeon E5-2620 v2。

       如果站在普通用户的角度看,这个结果差强人意,性能领先幅度也就45%左右,要知道从价格上看Intel Xeon E5-2680 V4可是甩E5-2620 v2几条街的。

       不过考虑到sysbench只是对CPU的部分指标考量,并不能代表全部,慎重起见,需要综合CPU的架构、主频、L3缓存、线程数和总线规格一起分析。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第33张图片

图26:E5-2680/E5-2620对比


       上图26是E5-2680 V4和E5-2620 V2对比,E5-2680单个线程实际使用主频虽只有1292MHZ,但实际性能胜过频率更高的E5-2620。

       怎么回事,难道E5-2680装上了涡轮增压?1.2T的发动机爆发出超过2.0L自然吸气的动力?这里引出新的问题,对于普通云计算使用者或企业上云用户,开通一个虚拟机,是关心高主频,还是看多核心?

       小朋友才做选择,让我选,我两个都要,高主频多核心都是王道。不过现实啪啪啪打脸,你不得不考虑成本,贵的东西自然好,但浪费资源也没必要。所以你需要先明确在虚拟机上要部署什么应用、什么使用场景、处理负载多大、是否计算密集型等,还有种特殊情况,有的应用或业务对CPU指令集有要求,比如有的软件要求AVX指令集支持,这样IvyBridge架构的CPU就不能选择了,必须选择Broadwell以后的CPU。

       除了主频,CPU的性能还和CPU的架构、工作频率、Cache大小、支持指令系统等有重要的影响,单一看CPU主频不准确。

       现代计算或标准型虚拟机大多数使用场景可能是多核多线程并行处理,需要CPU更快更大的缓存来暂存海量数据,这个时候CPU频率反而其次。

       总之一句话,CPU性能先看架构,再看主频,然后对比核心线程,最后再看缓存。

    2)不同线程数的性能对比

       我曾经看过一些利用sysbench测试CPU的错误例子,操作者会把测试参数中的--threads不断加大,甚至远超过虚拟机CPU总核数。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第34张图片

图27:threads超过CPU线程数后的表现

       多核CPU在多线程运用下,CPU的运算能力线性提高,如上图27所示,但当thread超过cpu核数后,CPU运算速度不会再增加。

       通常在实际使用场景中,如果是单核CPU运行多线程技术,执行多线程运算是没有效果的,而当云主机需要执行其他IO操作的任务, 比如读取文件、网络通讯等, 多线程技术才能发挥作用,因此多核CPU在多线程运用下,CPU运算能力会明显提高。就好比你就两条腿怎么跑的过四条腿,你又不是美国队长。

       threads不断增加,远超过虚拟机CPU总核数后,性能不会提升,只会增加这个CPU平均负载,如下图28,load average可以看到,在4核CPU虚拟机中,运行8个线程sysbench,导致sysbench线程互相争抢CPU使用权,负载达到8以上,造成严重负载,影响操作系统运行速度。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第35张图片

图28:超线程运行导致平均负载增加

     3)和公开数据对比

        很多时候在调查性能过程中,利用别人的经验,参考别人的数据不安全是坏事,自己闭门造车也要不得,当然这个过程需要慎重进行。

        基于PM测试的全球CPU天梯榜,该排名是基于数千个PerformanceTest基准测试得出的评分,相对公正,如下图29所示:

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第36张图片

图29:CPU天梯榜


        除此之外,推荐1个基准测试网站:openbenchmarking.org,这是1个测试结果数据的公共和私有存储网站,用于共享结果和有效比较多个测试结果集的有效协作。在这个网站有来自全球各地主动推送上来的测试数据。


     4)虚拟机的cpu性能损耗

       虚拟机cpu性能相比宿主机硬件的损耗程度,也是一个用户关心的:我们创建的KVM虚拟机能实现“零损耗”么?

       这是一个比较“庞大”的话题,无论使用哪种flavor(规格)创建虚拟机,它的vCPU在宿主机上物理CPU不同核之间调度。在虚拟机上,执行sysbench,命令请求下去后,先经过vCPU到虚拟机os,再调度到物理机CPU上,vCPU在物理机系统上就是1个线程调用,有调度就会产生开销,有了开销必然带来性能上的影响。

测试10秒钟,分析数小时,CPU性能只看跑分可不够_第37张图片

图30:物理CPU、云主机vcpu性能对比

       参考上图30中的数据,在同等硬件环境、相同工具下,虚拟机cpu素数运算性能与物理节点的CPU性能几乎“一致”,但这里的“一致”,是有前提的,需要保证在执行测试过程中不会受到其他虚拟机干扰,因为通常虚拟机 vCPU 是随机共享,vCPU 的共享方式会根据节点宿主机上其他主机的负载,将 vCPU 调度到不同核心上,当然也可能在同一核心上。

       不过这样对比并不公平,过于理想化。因为一个宿主机资源不可能只提供给1台虚拟机使用,绝大多数的情况下,单个计算节点上同时有多台虚拟机存活,他们互相争抢CPU的使用权,另外,宿主机所在Linux系统还可能会将内存交换、软中断等进程调度到虚拟机正在使用的物理核上,这些因素叠加必然会导致虚拟机相对于宿主机CPU性能产生抖动(性能抖动又是另外一个话题了)。


四 总结

       测试10秒钟,分析数小时,一个sysbench测试CPU的过程,看起来不起眼,但很多地方仍然存在疑惑,还需再挖掘。

       从工具执行原理,到操作系统内核影响,涉及编译器,再到cpu指令集,还有vcpu或qemu-kvm在宿主机的进程调度带来的性能开销。各个环节,每一个单独拿出来都是长篇大论。

       就目前已知的性能问题发现或瓶颈看,可以尝试四个方向的性能调优方案:

  (1)CPU独占

      这是目前各大云计算厂商常用的方法,前文也提到同一个宿主机节点的cpu资源被多个虚拟机争抢,且因为cpu的抖动,增加cpu切换,导致虚拟机计算能力的不稳定。如果使云主机机独占物理CPU,虚拟机的vCPU能够固定绑定到宿主机的指定CPU上,在整个运行期间,避免CPU浮动,减少CPU切换开销,能够一定程度上提高虚拟机CPU计算性能。

  (2)编译器优化

      不久前华为发布最新产品P30,这款手机因为各种段子火了,另一个火的就是他家的方舟编译器,号称可以实现 Android 性能革命。

      不要小看编译器,它是应用程序和CPU硬件(指令集)之间的桥梁。GCC是目前广泛应用的Linux系统的默认编译器,在很多Linux桌面发行版、服务器版上都能看到它的身影,但是在各硬件平台上却并不是性能最优的编译器。

      这里有2个方向:

n  取消GCC自动编译,采用人工编译,开启GCC全部优化项

n  直接用intel自家开发的ICC取代GCC, Intel基于x86架构CPU开发出ICC,能够最大程度发挥x86架构CPU的真实性能

  (3)bios调优,启用睿频

       曾经有过启用睿频大幅提升存储io性能的经历,那时对io请求进程做了频率上限导致性能被压制。开启睿频,可以让CPU根据实际运行程序的需求,动态增加运行频率用来提高处理器的性能。

  (4)sysbench库函数clock_gettime调用

       sysbench对clock_gettime调用频繁,这虽然与它本身设计有关系,但如果通过修改源码让其调用更少库函数的次数,也是未来的调优方向。