在高性能电脑上面,通过 CPU 对应的方式来进行进程优化的行为
高性能电脑集群 (cluster) 经常用在比较需要大量运算的环境中。另外,近期因为云端虚拟化的应用广泛,机房内的机器常常也是属于这种大型高性能电脑。 这种类的电脑经常有多颗 CPU 同时在运作,而大家知道 Intel 的 CPU 通常有 HT 功能,那其实你仅有一个运算单元仿真出两颗 CPU, 这时候到底系统要怎么操作才能达到优化?这就需要一些基础技能与特殊操作技巧了!
鸟哥在这里先说个大前提,免得大家以为在这篇看完后,自己的 Server 就会变得超级强!其实...并不会~
鸟哥的专业背景其实是环境工程,多年前为了了解空气污染排放与周界空气品质的相关性,因此开始接触空气品质模式, 这是一个美国环保署开发的模式,称之为 CMAQ (注1)。
因为 CMAQ 是个很大型的数值模式,因此单纯使用一颗 CPU 来跑是很不够力的!所以 CMAQ 原本就被开发为平行化处理, 也就是说,当模式运作时,它将整个大气空间分为三个维度 (水平的 X 与 Y ,还有垂直的 Z),然后依据 CPU 的内核数, 将这个空间再细分为多个格点 (grid),然后将这些格点平均的分给各个 CPU 去跑。
承上,你会以为 CPU 数量越多,那么这个模式的运作会越快~其实也不尽然~因为这些空间是有相关性的,所以格点与格点之间需要经常的交换数据~ 而你如果将它看在电脑系统上,那就会发现到每个 CPU 得要经常的交换数据,这些数据主要是在内存中,或者是网络上!
嗄!在内存中我们还能理解,为何是会在网络上呢?这是因为每部电脑的 CPU 内核数可能没有这么多,而你想要将这些 grid 分散到越多 CPU 内核上, 所以就动脑筋将数台电脑串接在一起,通过网络将这些 grid 分配到不同的电脑上,让这些电脑分别去跑这一个模式。也就是说, 如果你只有一般 PC 而已,那么多台 PC 串在一起,你也可以使用到超过 10 个以上的 CPU 内核数了!
这样就产生一个问题,因为 CPU 与内存的速度超级快,但是网络的速度却只有 Gigabit 或 10Gigabit,大约是 125Mbytes/s 或 1250Mbytes/s, 速度远远慢于一般 2GHz CPU 至少 16Gbytes/s 的速度,也慢于 DDR3 1333MHz 10Gbytes/s 的速度!所以,当使用网络上的 CPU 内核数 (亦即将一个工作丢给太多不同的电脑运算) 太多时,由于数据交换的问题,反而会导致系统性能的低落,也就是模式运算速度反而变慢了!
如上图的示意,每个 CPU 假设为一部单独的服务器,而我的一个 job 丢给四部系统去跑,但这四部系统是通过速度相对慢的 switch, 这时系统性能就卡在那个 switch 上面!但是也还是要考虑到数据的交换~如果数据交换没有这么大,丢给四部来跑可能还是可以增进性能的! 看看底下的讨论~
所以说,可以平行化运算时,你还是得要注意到,并不是 CPU 内核数使用的越多越好!你还得要思考:
所以说,到底你要怎么进行你的平行化模式的运作优化?每一个模式的案例都不一样!这得要自己追踪查找一下!以鸟哥为例, 早期 v4.7 的 CMAQ 版本不能用太多 CPU ,如果超过 8~16 颗以上,性能反而会变差~ 但新版 v5.1 的 CMAQ 版本,CPU 数量可以使用到 30 颗左右,性能可以持续增强哩!光是同一个 CMAQ 的不同版本, 在操作上面就有不一样的情况发生!
那鸟哥是怎么知道上述的情况?没啥特殊的~就是多次进行案例的运行,慢慢归纳出来的结果!这没办法由单一几次的测试就知道啦! 唉~性能调校也是一件相当大的工程!
现在不论个人电脑组装的服务器还是一般服务器等级的电脑,大部分的 CPU 主要是以 Intel 为主,所以底下我们针对 Intel 的 CPU 来说明一下。 再次强调,这篇主要针对的是高性能的系统,跟一般朋友们接触到的 server 运作环境可能差异很大!因此这篇文档的内容不见得适用于你喔!^_^
Intel 的 CPU 有个很有趣的功能,叫做超线程 (Hyper-Threading, HT 注2) 技术!他可以将 4 内核的单一颗 CPU 仿真出 8 个内核! 哇!真的是好棒棒!那到底是怎么办到的?强者我同事蔡董大大曾经说过,所谓的 HT 技术,简单的说,就是单一运算内核有两组寄存器,每个寄存器可以被视为一个实体的 CPU 内核这样而已。简图是这样:
其实说到底,这颗 CPU 还是只有四个基本的实体运算单元,但是由于各有两组寄存器,而 CPU 运算单元读取的数据就是来自于寄存器,因此两组寄存器就可以被操作系统判别为两颗逻辑 CPU 了。 因此我们会说 CPU 的 HT 技术,可以让你有 4 核 8 绪,就是这个原理。
为啥要讲这?来来来~我们前一小节谈到鸟哥的 v4.7 CMAQ 模式中,模式的运作并不会吃掉全部的 CPU 资源,由于这个特性, 因此图 1.2-1 当中那个运算内核就会有段时间保持闲置,这时从另一组的寄存器来的要求,就能够同时的运作 (因为 CPU 并没有在忙碌中)! 因此 v4.7 的模式运作中,鸟哥发现 4 核 8 绪真的很有用!可以发挥到 8 个 CPU 的性能!真是感谢 HT 啊!
但是 v5.1 之后就不是这么回事!因为每个 process 都要使用 100% 的 CPU 实体运算单元,因此 CPU 实体永远在忙碌!此时另一组寄存器如果加进来, 虽然不至于拖垮整体 CPU 效率,但是就是没有办法增加性能!因此使用 4 颗跟使用 8 颗 CPU 的结果,模式性能是差不多的!但如果使用的是 4 颗与 7 颗呢? 性能反而是 4 颗的比较好!很意外吧! ^_^
这也就是说,HT 技术对于不同的使用环境是具有不一样的情况的!如果是一般 server 的运作,因为 server 的服务大多不会占用 CPU 内核资源到 100%,因此 HT 一定要开!对于性能肯定有很大的帮助!因为他会让闲置中的 CPU 内核发挥到完整的运算能力 (其实一般 server 并不会太操 CPU 啦!)。
但如果你是像鸟哥这样操作大型数值模式时,动不动就需要用到 12~30 颗的实体运算 CPU 内核,那么 HT 技术的使用反而需要注意! 否则可能容易发生大问题!例如你以为四核八绪的环境下,你只要使用 4 颗 CPU 就好啊!那不是就会用到 4 个运算单元? 但如果你的操作系统不够聪明,反而是 4 个 process 用在两个实体运算的环境下,那反而产生两个闲置的 CPU 耶!性能差一倍!你说要命嘛?
你会问:『鸟哥,那 HT 技术到底要不要打开啊?』鸟哥回答你,一定要打开!就鸟哥最近这几周的经验来看, 鸟哥开了 HT 技术后,同时跑两个案例,每个案例占用 4 颗 CPU (平均分配在每个实体运算单元上),假设 0, 1, 2, 3 为实体, 4, 5, 6, 7 个别为 0, 1, 2, 3 的 HT (其实没有主副之分,反正原则上都是一组寄存器对应到实体运算单元),那两个案例就分别占用 0~3 以及 4~7。 结果呢?两个案例都能够使用到原本的 60% ~ 70% 的性能,那两者相加的情况下,可以到达 120%~140% 的性能!好棒棒! 不过有个重点,那就是『不要一个案例用到超过实体运算单元』的数量!这样对系统性能比较好!
过去桌机的年代,鸟哥没有钱~所以买到的 CPU 能超频就超频,可以省下好多好多钱!所谓的超频就是将 CPU 原本的时脉调高的一种技巧!当时很多主板的 BIOS 都有内置调整 CPU 时脉的功能! Intel 很有趣的是,他让系统可以自动超频!CPU 本身就具备超频的功能!只要操作系统支持就可以!这就是 Intel Turbo Boost 的技术! 对于鸟哥来说,CPU 更高的时脉,代表鸟哥可以节省更多的时间!
不要觉得不怎么了不起~以鸟哥的一款 E5-2420 v1 CPU (注3)来说,基本时脉为 1.9GHz,但自动超频最高可达 2.4GHz,如果全部内核都超频, 那最多也能够到达 2.2GHz。你以为 1.9 -> 2.2 没差多少嘛?换算一下百分比, (2.2-1.9)/1.9,可以达到 15.8% 的性能增加耶! 一般的性能调校能够增加 5~10% 就是很了不起的任务了,turbo boost 随便就增加了 15% 的性能!能不开嘛?当然要开!对吧!
对于 turbo boost 来说,看 Intel 网页的说明可能不是很准~因为网页给的是最大超频能耐~事实上超频到多快,还跟整体的功率消耗有关! 越多内核同时超频的速度越低!同样以 E5-2620 (6核12绪) 来说,当超频一个内核时,可以到达 2.4GHz,超频 2~3 内核可以到 2.3GHz,但超过 4 个以上, 就只能到达 2.2GHz 啰!所以详细的超频速度,还是得要实际侦测一下才知道的!
早期电脑的 CPU 就是单核单绪,使用上非常简单~后来因为多内核 CPU 产生了,于是开始有了可以支持多 CPU 的操作系统, 那就是 SMP (Symmetric Multi Processing)。在这种多处理器的架构下,每个 CPU 的地位都是相同的, 这些 CPU 都连接到同一个共享的主内存 (RAM) 上面,也因为如此,因此操作系统的每个 process 都可以在不同的 CPU 上面运作, 而这些 process 也能够在各个 CPU 之间轮流/交换运行,以期达到 CPU 的负载平衡状态。
不过,CPU 到内存间的距离实在是够长~导致 CPU 要去主内存取得数据时,都会花费很大的时间成本! 强者我同事蔡董大大说过,他们以前在交通大学设计 IC 时,只要能够将所有的东西都丢进 IC 的话,那速度就是超级快! 不过,这个芯片的空间是有限的,没办法包山包海,因此只好让每个组件独立了!再通过主板连接在一块~ 但是,只要是通过主板来达成连接,就有速度性能的极限~所以, CPU 只要去内存内读数据,就是会花费不少的时间成本。
因为这样,因此近来的新型 CPU 设计上,都会在 CPU 芯片里面放入一些高速缓存,让 CPU 常用的数据或指令放置在 CPU 内部的高速缓存, 这样 CPU 就可以减少去主内存拿数据的时间,以加快系统的性能!相关的 CPU 内的高速缓存可以下图来示意:
如上图所示,CPU 的每个运算单元都有自己的独立的第一层缓存,之后会有跟隔壁的运算单元共享的 L1 与 L2 缓存, 之后全部的运算单元会共享第三层缓存!如果数据全部都是在第三层缓存内,呵呵!那么 CPU 就不须向主内存要数据, 那运行的速度说有多快就有多快!
但是因为单纯的 SMP 机制是将所有的 CPU 放在同一个地位上,每个 CPU 运算单元假设都不知道有自己的高速缓存存在, 而是通过共用主内存来操作系统,这样就让这些高速缓存失去基本的功能了!因为所有的数据都会从主内存去抓取! 这样真是太可惜了吧!
所以为了让系统的性能可以提升,后来就有所有的非均匀内存访问模式 (Non-uniform memory access, 注4)。 NUMA 这个机制最大的特点就是要让 CPU 尽量读取自己的非分享的高速缓存 (集中在 L1/L2 ) ,让系统效率增加!然后在一段时间后, 在将数据拷贝到共享的内存 (例如第三层缓存或主内存中)。
除此之外,NUMA 机制也能够找出实体的运算单元,并且依据运算单元与寄存器之间的关系,来取得 HT 的相关性, 所以 NUMA 能够将资源平均的放置在不同的实体运算单元上,并且通过非共享的高速缓存,让整个性能得到合理的应用!
根据前面提到的,NUMA 其实就是要让系统的资源得到合理的分配,而我们知道 Intel CPU 的第三层缓存通常就是所有的运算单元共享! 而且速度比主内存快的多!那现在我们来思考一下,如果你的主板上面是可以安装两颗 CPU 封装的情况下,或者是可以安装四颗 CPU 封装的高端主板, 那通过 NUMA 机制来考量的话,你就会知道,NUMA 当然会希望让数据在同一个 CPU 封装内进行运行,尽量不要让数据又跑到不同的 CPU 封装去! 例如底下的图标案例:
如上图所示,共可以安装四颗 CPU 封装,为了让性能可以增加,因此 NUMA 就将整个系统的运算资源分为四个节点 (node), 那么当系统需要进行负载平衡时,大多可以在同一个 node 内进行,感觉上就是在单一 CPU 插槽上面进行数据搬移这样。 等到过一段比较长的时间,在将数据在各个 node 之间进行同步或拷贝,以方便进程在这些 node 之间交换。 也就是说,一个插槽可以被视为一个 node,因此单颗 CPU 封装的主板,最多就只会有一个 node, 两颗 CPU 插槽就有两个 node,以此类推!
不过,就以鸟哥的模式操作行为来说,其实鸟哥的空品数据实在太庞大,L3 缓存实在也不够用~因此, NUMA 对鸟哥来说, 其实是判断实体内核之间的相关性比较重要!鸟哥是建议启动 HT 的,那模式的运行又最好不要超过实体运算内核数, 所以该如何判断实体内核对应的 CPU 编号呢?这就是 NUMA 的重要性!
了解了基本的 CPU 功能 (HT 内核相关行、turbo boost 技术) 以及 NUMA 的架构后,再来总是得要查一下你的系统里面 CPU 到底这些功能开了没? 要如何调查?该如何启动或关闭等等的~来查一查啰!
说实在的,鸟哥好久没有用过 AMD 的 CPU 了,手边也没有相关的比较新的 AMD 的 CPU,所以很抱歉,只能就 Intel 的 CPU 来做个介绍。 上面提到许多 CPU 的功能,其实主要是 HT 技术与 turbo boost 技术,这两个技术都必须要在 BIOS 里面启动才行。 不过在已经开机的系统中,我们还是可以借由一些工具软件来查找的。
最简单的查找方式,就是通过 /proc/cpuinfo 搭配 Google 来查找 CPU 型号的特性。以鸟哥的两部使用中的设备来说, 如果查看 /proc/cpuinfo 会出现这个情况:
[student@localhost ~]$ cat /proc/cpuinfo processor : 7 vendor_id : GenuineIntel cpu family : 6 model : 58 model name : Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz stepping : 9 microcode : 28 cpu MHz : 1600.000 cache size : 8192 KB physical id : 0 siblings : 8 core id : 3 cpu cores : 4 apicid : 7 initial apicid : 7 fpu : yes fpu_exception : yes cpuid level : 13 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr ..... bogomips : 6784.34 clflush size : 64 cache_alignment : 64 address sizes : 36 bits physical, 48 bits virtual power management: |
比较重要的项目有这几个:
所以由上述的 processor, core id 就能够对应出来哪两个 CPU 号码是使用同一个实体 CPU 运算单元啰。 你也可以简单的使用底下的指令来查找实际的对应:
[student@localhost ~]$ cat /proc/cpuinfo | egrep '^processor|^core id' processor : 0 <== 第 0 号 CPU 号码 core id : 0 <== 第 0 个实体 CPU 内核 processor : 1 core id : 1 processor : 2 core id : 2 processor : 3 core id : 3 processor : 4 <== 第 4 号 CPU 号码 core id : 0 <== 第 0 个实体 CPU 内核 processor : 5 core id : 1 processor : 6 core id : 2 processor : 7 core id : 3 |
所以使用这个系统为例,鸟哥这个系统共有 4 个实体内核,编号为 0~3,其中 CPU ID 号码 0, 4 都是对应到同一个 CPU 实体运算单元! 这样说起来,这个系统确实有启动 HT 技术,而且我们也找到了实际的 CPU 运算单元啰!理解了吧?
但是在双插槽的设备中,可能使用上述的指令会出现重复的 core id 喔!因为有两个插槽之故。所以可能得要变成底下这样的指令来查找:
[student@localhost ~]$ cat /proc/cpuinfo | egrep '^processor|^physical|^core id' processor : 0 <== 第 0 号 CPU ID A0 physical id : 0 <== 第 0 号 CPU 插槽 A0 core id : 0 <== 第 0 号 CPU 运算内核 A0 processor : 1 <== 第 1 号 CPU ID B0 physical id : 1 <== 第 1 号 CPU 插槽 B0 core id : 0 <== 第 0 号 CPU 运算内核 B0 processor : 2 physical id : 0 core id : 1 processor : 3 physical id : 1 core id : 1 processor : 4 physical id : 0 core id : 2 processor : 5 physical id : 1 core id : 2 processor : 6 physical id : 0 core id : 3 processor : 7 physical id : 1 core id : 3 processor : 8 physical id : 0 core id : 4 processor : 9 physical id : 1 core id : 4 processor : 10 physical id : 0 core id : 5 processor : 11 physical id : 1 core id : 5 processor : 12 <== 第 12 号 CPU ID A1 physical id : 0 <== 第 0 号 CPU 插槽 A1 core id : 0 <== 第 0 号 CPU 运算内核 A1 processor : 13 <== 第 13 号 CPU ID B1 physical id : 1 <== 第 1 号 CPU 插槽 B1 core id : 0 <== 第 0 号 CPU 运算内核 B1 processor : 14 physical id : 0 core id : 1 processor : 15 physical id : 1 core id : 1 processor : 16 physical id : 0 core id : 2 processor : 17 physical id : 1 core id : 2 processor : 18 physical id : 0 core id : 3 processor : 19 physical id : 1 core id : 3 processor : 20 physical id : 0 core id : 4 processor : 21 physical id : 1 core id : 4 processor : 22 physical id : 0 core id : 5 processor : 23 physical id : 1 core id : 5 |
根据上述的表格数据,我们可以发现到 A0, A1 为相同插槽的相同运算单元,因此 CPU 号码 0, 12 为同一个插槽的同一个运算单元! 就是互为 HT 技术的两个同一颗 CPU 内核啰,同理,B0, B1 为另一个插槽的同一个运算内核,所以 1, 13 为另一个插槽的 0 号 CPU 内核, 这样可以看得懂了吗?
由于 /proc/cpuinfo 数据量比较多,查找起来还得要自己判断内核与编号的对应,很有点讨厌。我们可以通过 Linux 系统提供的 numactl 这个指令来调用出 CPU 参数喔!不过很多系统缺省都没有安装 numactl 的,因此你得要自己安装才行。 安装超简单,在 CentOS 6.x 或 7.x 都使用 yum install numactl 即可!装好之后,你可以使用底下的指令来查找系统的 CPU 信息啰!
[student@localhost ~]$ numactl --hardware available: 1 nodes (0) <==只有一个插槽 node 0 cpus: 0 1 2 3 4 5 6 7 <==共有 0~7 个 CPU ID 号码,对半分两分 node 0 size: 32632 MB node 0 free: 8247 MB node distances: node 0 0: 10 |
一般来说,cpus 的编号会等份的分成两个对应部分,因此 0~3 为一组,4~7 为一组,因此 0, 4 为同一个实体内核! 很快就能够判断出来。那如果是另一组双 CPU 插槽的系统呢?那他就会变成这样:
[student@localhost ~]$ numactl --hardware available: 2 nodes (0-1) <==有 2 个插槽 node 0 cpus: 0 2 4 6 8 10 12 14 16 18 20 22 <==第 0 号 CPU 封装的 CPU 号码,对半分两分 node 0 size: 49106 MB node 0 free: 13742 MB node 1 cpus: 1 3 5 7 9 11 13 15 17 19 21 23 <==第 1 号 CPU 封装的 CPU 号码,对半分两分 node 1 size: 49152 MB node 1 free: 47328 MB node distances: node 0 1 0: 10 20 1: 20 10 |
从上面的信息来看,第一个 CPU 封装 (0号) 号码为 0, 2, 4...到 22 号,对半分两半,所以 0,12 是同一个内核, 2, 14 是同一个内核, 4, 16 是同一个,以此类推。而 1, 13 则是另一个 CPU 插槽的内核了!这样是否也很容易判断呢?
Intel 的 HT 技术算是比较好找出来的信息,那个 turbo boost 就没有这么好找了。 基本上,CentOS 7 缺省内核就支持 turbo boost 了,你不用额外进行啥动作。
但是 CenOS 6.x 的版本中,如果你的系统是拿来跑数值模式的,可能你的前辈会跟你说:【千万不要升级系统】!这也是很无奈的, 因为很多模式的软件相依问题,会导致升级后你的模式可能会无法运作!所以不要随便升级系统喔! 因此,虽然新的 CentOS 6.x 缺省也支持 turbo boost ,但是旧版的缺省就没有启动 turbo boost 啰! 因此你得要自己检查检查才行。
你的内核得要有相关的模块才能够支持这个技术的!要如何确认?这样做就好了!
# 只有 CentOS 6 才需要确认这部分,CentOS 7 缺省支持了 [root@localhost ~]# ll /lib/modules/$(uname -r)/kernel/arch/x86/kernel/cpu/cpufreq -rwxr--r--. 1 root root 23328 2016-07-13 02:51 acpi-cpufreq.ko <==这个 -rwxr--r--. 1 root root 40376 2016-07-13 02:51 intel_pstate.ko <==这个 -rwxr--r--. 1 root root 5848 2016-07-13 02:51 mperf.ko -rwxr--r--. 1 root root 12320 2016-07-13 02:51 p4-clockmod.ko -rwxr--r--. 1 root root 18576 2016-07-13 02:51 pcc-cpufreq.ko -rwxr--r--. 1 root root 41576 2016-07-13 02:51 powernow-k8.ko -rwxr--r--. 1 root root 13144 2016-07-13 02:51 speedstep-lib.ko |
上面两个模块都可以激活 turbo boost,但是只能启动一个而已,不可以同时启动。如果两个都没有启动,那你就可以自由启动任何一个! 启动的方式为:【 modprobe acpi-cpufreq 】或【 modprobe intel_pstate 】。要观察目前启动的是哪一个模块,可以这样做:
# CentOS 6 的两种输出结果 [root@localhost ~]# lsmod | egrep 'intel_pstate|acpi_cpufreq' acpi_cpufreq 7763 0 freq_table 4936 2 cpufreq_ondemand,acpi_cpufreq mperf 1557 1 acpi_cpufreq [root@localhost ~]# lsmod | egrep 'intel_pstate|acpi_cpufreq' intel_pstate 14628 0 |
上面是两部不同的系统启动了不同的模块的情况。基本上,出现上面任何一个画面都可以视为 turbo boost 已经启动了! 如果是 CentOS 7 的系统,可能就会变成如下的模样:
# CentOS 7 的输出结果 [root@localhost ~]# lsmod | egrep 'intel|acpi' intel_powerclamp 18648 0 intel_rapl 18773 0 acpi_pad 116305 0 acpi_power_meter 18087 0 |
基本上都会出现上述的模块啦!那应该就是与 CPU 时脉自动调整有关的模块。 接下来要来聊聊,那么这个 CPU 的加速机制中,你需要的是【 CPU 省电模式】 还是【 CPU 性能模式】呢?
在加载了上述的模块后,现在 CPU 的时脉已经可以调整了。不过,某些模块并不会将实际的 CPU 时脉列出在 /proc/cpuinfo 上, 所以你不应该仅查看 /proc/cpuinfo 来确认时脉的。好了,那么先来看看你目前的 CPU 时脉调整是使用甚么机制呢? 要查阅 CPU 时脉机制,你应该要安装如下的软件才行:
自己使用 yum 去安装吧!装好之后你就可以这样做了:
# 1 先确认一下目前的 CPU 时脉相关设置,i7 3770 CPU [root@localhost ~]# cpupower frequency-info analyzing CPU 0: driver: acpi-cpufreq CPUs which run at the same hardware frequency: 0 1 2 3 4 5 6 7 CPUs which need to have their frequency coordinated by software: 0 maximum transition latency: 10.0 us hardware limits: 1.60 GHz - 3.40 GHz available frequency steps: 3.40 GHz, 3.40 GHz, 3.30 GHz, 3.10 GHz, 3.00 GHz, 2.90 GHz, 2.80 GHz, 2.60 GHz, 2.50 GHz, 2.40 GHz, 2.20 GHz, 2.10 GHz, 2.00 GHz, 1.90 GHz, 1.70 GHz, 1.60 GHz available cpufreq governors: ondemand userspace performance current policy: frequency should be within 1.60 GHz and 3.40 GHz. The governor "ondemand" may decide which speed to use within this range. current CPU frequency: 1.60 GHz (asserted by call to hardware) boost state support: Supported: yes Active: yes |
上面是鸟哥的 i7 3770 CPU 系统上面的输出情况,系统缺省不是使用 intel_pstate 模块,而是使用 acpi-cpufreq 模块, 相当有趣!而这个模块提供了三种 CPU 时脉管理方式,分别是 ondemand, userspace 以及 performance 模式! 目前使用的是 ondemand(用多少算多少)。如果你需要加强系统性能,可能就得要将他改成 performance 模式才对! 另外来看一下,如果是 intel_pstate 模块的结果又是怎样?
# 1. 另一部双插槽 CPU 系统的结果 [root@localhost ~]# cpupower frequency-info analyzing CPU 0: driver: intel_pstate CPUs which run at the same hardware frequency: 0 CPUs which need to have their frequency coordinated by software: 0 maximum transition latency: Cannot determine or is not supported. hardware limits: 1.20 GHz - 2.40 GHz available cpufreq governors: performance powersave current policy: frequency should be within 1.20 GHz and 2.40 GHz. The governor "performance" may decide which speed to use within this range. current CPU frequency: 2.20 GHz (asserted by call to hardware) boost state support: Supported: yes Active: yes 2300 MHz max turbo 4 active cores 2300 MHz max turbo 3 active cores 2400 MHz max turbo 2 active cores 2400 MHz max turbo 1 active cores |
输出的结果差不多,但是可用的时脉机制只有 performance 以及 powersave 两种,其中 powersave 是比较省电的模式, 所以除非运作的程序会跑很久,否则通常 turbo boost 不会开到太大!所以如果你是真的需要性能的环境, 可能还是得要改成 perforamnce 才对!鸟哥这个系统拿来跑模式,所以已经被鸟哥改成 performance 模式了。 同时消息也指出,使用 1, 2 颗 CPU 内核时,最大超频可达 2400MHz,但是 3 颗以上最多只剩下 2300MHz 啰! 这就是 turbo boost 的信息。
如果你原先的环境是 powersave 想改成 performance 时,怎么做呢?这样做即可:
# 2. 更改 CPU 时脉变更机制 [root@localhost ~]# cpupower frequency-set --governor performance |
既然我们说 /proc/cpuinfo 里面的时脉可能并非真实的时脉,那么又该如何观察实际的时脉呢?同样使用 cpupower 来处理即可! 很简单的观测行为!
[root@localhost ~]# cpupower monitor |Nehalem || Mperf || Idle_Stats CPU | C3 | C6 | PC3 | PC6 || C0 | Cx | Freq || C0 | C1-I | C1E- | C3-I | C6-I 0| 0.68| 99.19| 0.00| 0.00|| 0.02| 99.98| 2019|| 0.00| 0.00| 0.02| 0.00| 99.96 4| 0.68| 99.20| 0.00| 0.00|| 0.02| 99.98| 2787|| 0.00| 0.00| 0.00| 0.00| 99.98 1| 36.18| 23.61| 0.00| 0.00|| 2.13| 97.87| 1640|| 0.00| 0.01| 35.43| 25.17| 37.38 5| 36.18| 23.61| 0.00| 0.00|| 0.04| 99.96| 1978|| 0.00| 0.00| 0.00| 0.05| 99.90 2| 0.00| 99.78| 0.00| 0.00|| 0.01| 99.99| 1965|| 0.00| 0.00| 0.00| 0.00| 99.99 6| 0.00| 99.78| 0.00| 0.00|| 0.11| 99.89| 3185|| 0.00| 0.00| 0.04| 0.00| 99.82 3| 0.56| 99.37| 0.00| 0.00|| 0.02| 99.98| 2793|| 0.00| 0.00| 0.00| 0.00| 99.98 7| 0.56| 99.38| 0.00| 0.00|| 0.01| 99.99| 1916|| 0.00| 0.00| 0.00| 0.00| 99.99 [root@localhost ~]# watch -n 2 "cpupower monitor" [root@localhost ~]# watch -n 2 "cpupower monitor -m Mperf" |
单一观察行为直接输入 cpupower monitor 即可,如果想要使用类似 top 的功能持续观察,那就使用 watch -n 2 "cpupoower monitor" 这样的指令行为来处理即可。
在输出的信息中,比较重要的就是在 Mperf 的项目中,那个 C0 代表目前使用的 CPU 资源 (从 0 ~ 100), Cx 则是 CPU 的闲置百分比。另外, Freq 就是目前的实际内核时脉了!其他的项目有兴趣的查一下 manual, 否则就直接先略过,光看 Mperf 即可。因此,如果你只想要知道 Mperf 的话,就使用上述的『cpupower monitor -m Mperf』 直接查看 Mperf 即可!
如果觉得上述的指令你都不太喜欢,那么还可以使用一个名为 i7z 的软件来观察喔!不过这个指令 CentOS 官网缺省没有提供! 有兴趣的朋友可以到底下的网站来下载自己安装啰!
如果你安装好了这个软件,那么直接输入 i7z 就能够看到类似底下的画面:
[root@localhost ~]# i7z Cpu speed from cpuinfo 3392.00Mhz cpuinfo might be wrong if cpufreq is enabled. To guess correctly try estimating via tsc Linux's inbuilt cpu_khz code emulated now True Frequency (without accounting Turbo) 3391 MHz CPU Multiplier 34x || Bus clock frequency (BCLK) 99.74 MHz Socket [0] - [physical cores=4, logical cores=8, max online cores ever=4] TURBO ENABLED on 4 Cores, Hyper Threading ON Max Frequency without considering Turbo 3490.74 MHz (99.74 x [35]) Max TURBO Multiplier (if Enabled) with 1/2/3/4 Cores is 39x/39x/38x/37x Real Current Frequency 3861.69 MHz [99.74 x 38.72] (Max of below) Core [core-id] :Actual Freq (Mult.) C0% Halt(C1)% C3 % C6 % C7 % Temp Core 1 [0]: 2275.03 (22.81x) 1 0 1 99.6 0 33 Core 2 [1]: 1674.44 (16.79x) 1 1.69 1 97.1 0 29 Core 3 [2]: 3809.78 (38.20x) 1 0 1 99.8 0 32 Core 4 [3]: 3861.69 (38.72x) 0 0.00312 0 100 0 31 C0 = Processor running without halting C1 = Processor running with halts (States >C0 are power saver) C3 = Cores running with PLL turned off and core cache turned off C6 = Everything in C3 + core state saved to last level cache Above values in table are in percentage over the last 1 sec [core-id] refers to core-id number in /proc/cpuinfo 'Garbage Values' message printed when garbage values are read Ctrl+C to exit |
里面特别提到 CPU 的时脉 (外频与倍频),由于这部系统仅有 4 个实体运算单元,因此就仅列出 4 个相关的信息。 比较需要注意的是实际时脉、C0%(实际运作的 CPU 百分比),以及 Temp (CPU 的内核温度),这些信息对管理员来说, 那就重要了吧!哈哈!所以鸟哥觉得 i7z 还算是很好用的一个工具软件哩!
现在来测试一下,到底 CPU 有没有启动 turbo boost 的技术?以及是否使用到了 HT 的技术这样。因为我们想要操 CPU, 所以让 CPU 来算 pi 可能是个不错的方案~现在请你打开三个终端机窗口,分别进行:
现在请下达底下的指令,来看看你的系统会出现什么事情?
[student@localhost ~]$ time echo "scale=3000; 4*a(1)" | bc -lq
|
如果单纯使用上述的指令,而且你没有运行其他额外的指令,那你会发现到,运算的 CPU 似乎会自己找到优化的位置, 而且可能会变来变去~不会一直使用同一颗 CPU 运算内核啦!那么分数与 CPU 时脉会有什么关系呢? 在鸟哥的 i7 3770 系统中,鸟哥利用上述指令算出来的时间是这样的:
你会觉得 performance 与 ondemand 差不多,但是实际上观察 CPU 时脉,会发现闲置时 CPU 的时脉显示中, performance 的时脉永远都留在高档 (2500MHz以上), 但是 ondemand 在 CPU 闲置时,会自动降频到大约 1600~2000MHz 左右。所以看起来,如果你需要高性能,而且你的环境也不缺电,那么使用 performance 没啥问题! 不过如果省电是你的考量,那么使用缺省的 ondemand 可能是最好的选择喔!
为了达到性能最大化,所以底下鸟哥使用的是 performance 这个 CPU 时脉机制喔!而且建议你不要使用 powersave 这个机制啦!性能太差!
现在让我们写一只脚本来测试系统的性能!因为我们的系统有 4 个实体内核,所以底下我们需要『同时』运作 4 个 pi 的计算~ 这时通过 shell script 会是比较简单的方式!
[root@localhost ~]# vim cpupi.sh
#!/bin/bash
for i in 0 1 2 3
do
time echo "scale=3000; 4*a(1)" | bc -lq &> /dev/null &
done
|
全部丢进系统去跑,很有趣的是,鸟哥持续运作数次,每次运作的 CPU 都不同,但是都能刚好使用到四个实体的 CPU !这算是好事好事! 只是虽然如此,每次使用的 CPU ID 还是不会相同!算出来的数据跟单一内核不太相同!因为 turbo boost 说驱动四个实体内核时,最高超频到 3700MHz, 而不是单一内核的 3900MHz,所以四颗算完的分数为 5.24 秒左右~
好了,那如果上述的脚本中,那个『 for i in 0 1 2 3 』后面又多加一个,变成『 for i in 0 1 2 3 4 』,同时跑 5 个计算呢? 算出来的结果会怎样?你会以为有 3 个很快,有 2 个会变得比较慢 (因为共用内核) 对吧?其实不然喔!鸟哥多次计算的结果,时间分布在 5.5 ~ 6.8 秒左右, 而且每个 CPU 开始会乱跑!这是因为系统有使用某些特别的机制 (后面介绍) ,因此会让努力让 CPU 保持在『随时都可以平衡』的状态! 通过这个机制,每个进程的时间都会比较相近 (至少保持平头式的平等就是了!)。
如果你想要指定某些特定的 CPU 帮你运行进程,那就得要使用 numactl 这个指令了 (通过 yum 安装 numactl 软件),让我们修改一下 cpupi.sh 这只脚本:
[root@localhost ~]# vim cpupi.sh #!/bin/bash for i in 0 1 2 3 do time echo "scale=3000; 4*a(1)" | numactl -C ${i} bc -lq &> /dev/null & done |
我们先指定 4 个进程来运行,现在你丢进系统跑的时候,每支进程就会固定由某个 CPU 去运作了!算出来的时间也差不多在 5.24 左右! 但如果通过指定的方式来运行 5 个进程,你就会发现到有 3 个进程跑出一模一样的 5.24 秒,但是有两个进程跑到了 8.3 ~ 8.4 秒左右! 虽然 CPU 保持在 100% 的运作情况,不过两个 pi 使用到同一个内核时,并不会是加总 (5.24+5.24=10.5) 的秒数,还是有比较快些!这就是 HT 的能力!
通过上面的测试,你会知道,其实 4 核 8 绪的 CPU 环境中,并不是每个 CPU ID 都是独立存在的!其实是有两组共四对运算内核! 如果你对于系统的操作是跟鸟哥一样,需要大量的运算环境!鸟哥建议还是自己指定 CPU 来运算比较好!让系统自己计算平衡时, 由于许多的数据会在 CPU 之间搬移,还是会损耗一些时间成本(这在 pi 的计算当中看不太出来)!鸟哥近来在模式的运算上,渐渐倾向于自己指定 CPU ID 啰! 至少数据不会乱跑~在数据交易时,会节省很多宝贵的时间。
你可能会问,既然 HT 技术其实使用的也只是同一个运算内核~那么有没有 HT 有差别嘛?这个问题其实我们可以简单的使用 pi 计算来了解喔! 首先,让我们来算一个单一内核的 pi 吧!
[root@localhost ~]# vim cpupi.sh #!/bin/bash for i in 3 do time echo "scale=3000; 4*a(1)" | numactl -C ${i} bc -lq &> /dev/null & done |
上述指令算出来的结果大约是在 4.98~5.00 秒之间~这是单一 pi 的计算~好!让我们来同时放两个 pi 在同一个 CPU ID 上面! 如同上面的指令,依旧丢在 CPU ID 3 上面吧!
[root@localhost ~]# vim cpupi.sh #!/bin/bash for i in 3 3 do time echo "scale=3000; 4*a(1)" | numactl -C ${i} bc -lq &> /dev/null & done |
你应该会发现到 top 上面出现两个 50% 使用率的 pi 啊!没办法达到 100% 了!而且,算出来的结果大约是 10.03 秒左右~ 差不多就是两倍 pi 的时间~这样看是很合理啦!现在将两个 pi 计算丢在 3 与 7 号的 CPU ID 上面,我们知道 3, 7 这两个号码共用同一个运算内核的喔!
[root@localhost ~]# vim cpupi.sh #!/bin/bash for i in 3 7 do time echo "scale=3000; 4*a(1)" | numactl -C ${i} bc -lq &> /dev/null & done |
有趣了!你会发现 3, 7 号的 CPU ID 都是 100% 去跑~跑出来的结果大约是 8.08 秒左右~ 因为共用运算内核,所以虽然是两个看起来独立的 CPU,但是就是无法达到 5 秒跑完~但是有趣了!它也不是两倍时间的 10 秒! 而是 8 秒左右~若以 10 秒为基准,那么 HT 节省了 20% 的时间耶!怪不得某些文章说,HT 可能会有 10%~30% 之间的性能增长! 这样简单的测试,你可以理解 HT 的好处了嘛?
早期的 Linux 因为要让 CPU 的负载能够平衡,所以会使用一个称之为 irqbalance 的机制,让所有的 process 在 CPU 之间轮流运作! 不过早期的 irqbalance 很笨,不太会区分不同的 CPU ID 是否为同一个实体内核,只会考量 CPU ID 而已。因此有的时候 irqbalance 运作后, 反而会让许多的进程卡在同一个实体内核中,造成系统性能的低落。
不过就上面的测试来看,现在 CentOS 6.8 以后的系统,irqbalance 似乎变聪明了,会自己判断实体内核的所在处,尽量让不同的进程分散到不同的实体内核去跑, 所以看起来性能还算相当不错。
但就双 CPU 插槽的系统来看,光是 irqbalance 判断是否为实体内核还是比较弱一些~如果能够让系统自己在 numa 的架构下尽量减少错误的内存读取时, 性能会再增加一些的。你当然可以几使用 numactl 去指定运行的 CPU 内核,不过如果每支程序你都得要指定,那就伤脑筋了! 这时鸟哥建议你激活 numad 这个进程即可!
不过需要注意的是,如果是单插槽 CPU 的系统架构,那么使用 irqbalance/numad 应该是差异不大! 有没有变动都不会差很多。但如果是双 CPU 插槽,那就真的建议要将 irqbalance 关掉,改成 numad 啰!
如果你一开始下达的指令中,分配给 CPU 的运算错误了!那该如何是好?中断程序?如果这个程序已经运算好一阵子了,重新运行实在很浪费时间! 有没有办法在进程运作的情况下,重新变更进程的 CPU ID 呢?可以的!通过 taskset 即可!让我们来运行一下底下的程序好了!
[root@localhost ~]# vim cpupi.sh #!/bin/bash for i in 0 1 2 3 do time echo "scale=6000; 4*a(1)" | numactl -C 0 bc -lq &> /dev/null & done |
鸟哥故意将运算的时间拉长 (3000 变成 6000,时间增加好几倍不只两倍!),然后错误的将所有的进程都丢到第 0 号 CPU 去!运行看看! 你会发现到 CPU 0 忙的要死~在 top 内的四个 bc 却只有 25% 的使用率...
怎么办呢?没关系~你可以先通过『 ps -eF 』找出进程与 CPU ID 的关系,然后再通过 taskset 来处理即可!
# 1. 先找出 CPU 与进程的关系 [root@localhost ~]# ps -eF UID PID PPID C SZ RSS PSR STIME TTY TIME CMD root 19765 19759 21 3252 956 0 13:40 pts/2 00:00:01 bc -lq root 19767 19760 21 3252 956 0 13:40 pts/2 00:00:01 bc -lq root 19769 19761 21 3252 952 0 13:40 pts/2 00:00:01 bc -lq root 19770 19763 21 3252 956 0 13:40 pts/2 00:00:01 bc -lq # 2. 将不同的进程丢到不同的 CPU ID 去! [root@localhost ~]# taskset -cp 1 19767 pid 19767's current affinity list: 0 <== 将 0 号改成 1 号 CPU 去运行啰! pid 19767's new affinity list: 1 [root@localhost ~]# taskset -cp 2 19769 pid 19769's current affinity list: 0 pid 19769's new affinity list: 2 [root@localhost ~]# taskset -cp 3 19770 pid 19770's current affinity list: 0 pid 19770's new affinity list: 3 |
其实比较重要的是 ps -eF 输出的 PID 与 PSR 这两个参数,一个是 PID 一个是 CPU ID 号码~然后通过 taskset 就能够更改 CPU 的运行啰! 现在你也能够控制系统 CPU 了!有够愉快吧!!哈哈!
高性能电脑也经常拿来作为虚拟化的基础系统,而我们知道虚拟化系统使用的 CPU 当然是通过实体 CPU 啊!而你的虚拟机的 CPU 通常就是使用 irqbalance 或者是 numad 进行动态调整,因此 VCPU 使用到的实体 CPU 其实是经常变动的。而因为 numad 的关系,通常不会让你的 VCPU 使用到相同的实体运算内核啦! 所以通常你只要打开 numad 让它帮你调整就好~
如果你是性能癖,那启动虚拟机时,最好能够调整一下你的 xml 文件内的 CPU 设置!最重要的是一行 CPU 使用实体 CPU 的设置值! 『 <cpu mode='host-passthrough'></cpu> 』这样让你的虚拟机能够直接看到实体机器的 CPU,这样的性能才会优化!
但是,如果你需要让虚拟机的 VCPU 与实体机器的 CPU 对应起来~举例来说,鸟哥这部 i7 3770 的机器上面跑一个虚拟机,里面用到了 4 颗 VCPU, 我可以这样观察 VCPU 与实体 CPU 的对应喔:
# 1. 观察一下虚拟机的 CPU 对应,这里假设我的虚拟机名称为 linux 喔!: [root@localhost ~]# virsh list Id 名称 状态 ---------------------------------------------------- 2 linux 运行中 [root@localhost ~]# virsh vcpuinfo linux VCPU: 0 <==虚拟机的 CPU 号码 处理器: 2 <==实体机器的 CPU 号码 状态: 运行中 处理器时间: 16.4s 处理器的同属: yyyyyyyy VCPU: 1 处理器: 0 状态: 运行中 处理器时间: 10.6s 处理器的同属: yyyyyyyy VCPU: 2 处理器: 4 状态: 运行中 处理器时间: 0.1s 处理器的同属: yyyyyyyy VCPU: 3 处理器: 3 状态: 运行中 处理器时间: 0.1s 处理器的同属: yyyyyyyy |
结果让人大吃一惊!实体 CPU 使用到的竟然是 2, 0, 4, 3,我们知道 0, 4 是同一颗 CPU 运算内核啊!所以这样的结果会让虚拟机的性能变差! 虽然上述的结果是会一直变更的~所以有的时候就会正常,有的时候就会不正常~唉啊!头好疼啊~怎办啊?没关系,我们可以通过一个对应的指令来解决这个问题:
# 2. 让虚拟机的 CPU 绑定实体 CPU [root@localhost ~]# virsh vcpupin linux 0 0 [root@localhost ~]# virsh vcpupin linux 1 1 [root@localhost ~]# virsh vcpupin linux 2 2 [root@localhost ~]# virsh vcpupin linux 3 3 [root@localhost ~]# virsh vcpuinfo linux VCPU: 0 处理器: 0 状态: 运行中 处理器时间: 120.6s 处理器的同属: y------- VCPU: 1 处理器: 1 状态: 运行中 处理器时间: 337.0s 处理器的同属: -y------ VCPU: 2 处理器: 2 状态: 运行中 处理器时间: 0.1s 处理器的同属: --y----- VCPU: 3 处理器: 3 状态: 运行中 处理器时间: 0.1s 处理器的同属: ---y---- |
不过根据鸟哥测试的结果,有时候你还是得要先关闭 numad 然后运行对应后再启动 numad 这样才会正确的对应到 VCPU 与 CPU! 经过这样的简单的举动,你的虚拟机可能会有很大的进步喔!毕竟虚拟机比较笨,他不会知道是否用到同一个内核~而实体机器有时候很忙, 也没有办法理解虚拟机对应的 CPU 是否有优化~所以某些时候为了取得比较好的操作经验,还是得要处理一下 CPU 的对应比较好!