Perf IPC以及CPU性能

Perf IPC以及CPU性能

为了让程序能快点,特意了解了CPU的各种原理,比如多核、超线程、NUMA、睿频、功耗、GPU、大小核再到分支预测、cache_line失效、加锁代价、IPC等各种指标(都有对应的代码和测试数据)都会在这系列文章中得到答案。当然一定会有程序员最关心的分支预测案例、Disruptor无锁案例、cache_line伪共享案例等等。

这次让我们从最底层的沙子开始用8篇文章来回答各种疑问以及大量的实验对比案例和测试数据。

大的方面主要是从这几个疑问来写这些文章:

  • 同样程序为什么CPU跑到800%还不如CPU跑到200%快?
  • IPC背后的原理和和程序效率的关系?
  • 为什么数据库领域都爱把NUMA关了,这对吗?
  • 几个国产芯片的性能到底怎么样?

系列文章

CPU的制造和概念

Perf IPC以及CPU性能

CPU性能和CACHE

CPU 性能和Cache Line

十年后数据库还是不敢拥抱NUMA?

Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的

Intel、海光、鲲鹏920、飞腾2500 CPU性能对比

一次海光物理机资源竞争压测的记录

飞腾ARM芯片(FT2500)的性能测试

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y6FG3udx-1628741390158)(https://plantegg.oss-cn-beijing.aliyuncs.com/images/951413iMgBlog/image-20210802161455950.png)]

程序性能

程序的 CPU 执行时间 = 指令数/(主频*IPC)

IPC: insns per cycle insn/cycles

CPU 流水线工作原理

cycles: CPU时钟周期。CPU从它的指令集(instruction set)中选择指令执行。

一个指令包含以下的步骤,每个步骤由CPU的一个叫做功能单元(functional unit)的组件来进行处理,每个步骤的执行都至少需要花费一个时钟周期。

  • 指令读取(instruction fetch, IF)
  • 指令解码(instruction decode, ID)
  • 执行(execute, EXE)
  • 内存访问(memory access,MEM)
  • 寄存器回写(register write-back, WB)

在这里插入图片描述

以上结构简化成流水线就是:

在这里插入图片描述

IF/ID 就是我们常说的前端,他负责不停地取指和译指,然后为后端提供译指之后的指令,最核心的优化就是要做好分支预测,终归取指是要比执行慢,只有提前做好预测才能尽量匹配上后端。后端核心优化是要做好执行单元的并发量,以及乱序执行能力,最终要将乱序执行结果正确组合并输出。

五个步骤只能串行,但是可以做成pipeline提升效率,也就是第一个指令做第二步的时候,指令读取单元可以去读取下一个指令了,如果有一个指令慢就会造成stall,也就是pipeline有地方卡壳了。

$sudo perf stat -a -- sleep 10  

Performance counter stats for 'system wide':  

 239866.330098      task-clock (msec)         #   23.985 CPUs utilized    /10*1000        (100.00%)  
        45,709      context-switches          #    0.191 K/sec                    (100.00%)  
         1,715      cpu-migrations            #    0.007 K/sec                    (100.00%)  
        79,586      page-faults               #    0.332 K/sec  
 3,488,525,170      cycles                    #    0.015 GHz                      (83.34%)  
 9,708,140,897      stalled-cycles-frontend   #  278.29% /cycles frontend cycles idle     (83.34%)  
 9,314,891,615      stalled-cycles-backend    #  267.02% /cycles backend  cycles idle     (66.68%)  
 2,292,955,367      instructions              #    0.66  insns per cycle  insn/cycles  
                                              \#    4.23  stalled cycles per insn stalled-cycles-frontend/insn (83.34%)  
   447,584,805      branches                  #    1.866 M/sec                    (83.33%)  
     8,470,791      branch-misses             #    1.89% of all branches          (83.33%)  

stalled-cycles,则是指令管道未能按理想状态发挥并行作用,发生停滞的时钟周期。stalled-cycles-frontend指指令读取或解码的指令步骤,而stalled-cycles-backend则是指令执行步骤。第二列中的cycles idle其实意思跟stalled是一样的,由于指令执行停滞了,所以指令管道也就空闲了,千万不要误解为CPU的空闲率。这个数值是由stalled-cycles-frontend或stalled-cycles-backend除以上面的cycles得出的。

另外cpu可以同时有多条pipeline,这就是理论上最大的IPC.

pipeline效率和IPC

虽然一个指令需要5个步骤,也就是完全执行完需要5个cycles,这样一个时钟周期最多能执行0.2条指令,IPC就是0.2,显然太低了。

  • 非流水线:
    在这里插入图片描述

如果把多个指令的五个步骤用pipeline流水线跑起来,在理想情况下一个时钟周期就能跑完一条指令了,这样IPC就能达到1了。

  • 标量流水线:标量(Scalar)流水计算机是只有一条指令流水线的计算机:
    在这里插入图片描述

进一步优化,如果我们加大流水线的条数,让多个指令并行执行,就能得到更高的IPC了,但是这种并行必然会有指令之间的依赖,比如第二个指令依赖第一个的结果,所以多个指令并行更容易出现互相等待(stall).

  • 超标量流水线:所谓超标量(Superscalar)流 水计算机,是指它具有两条以上的指令流水线, 超标流水线数量也就是ALU执行单元的并行度
    在这里插入图片描述

一般而言流水线的超标量不能超过单条流水线的深度

每一个功能单元的流水线的长度是不同的。事实上,不同的功能单元的流水线长度本来就不一样。我们平时所说的 14 级流水线,指的通常是进行整数计算指令的流水线长度。如果是浮点数运算,实际的流水线长度则会更长一些。
在这里插入图片描述

指令缓存(Instruction Cache)和数据缓存(Data Cache)

在第 1 条指令执行到访存(MEM)阶段的时候,流水线里的第 4 条指令,在执行取指令(Fetch)的操作。访存和取指令,都要进行内存数据的读取。我们的内存,只有一个地址译码器的作为地址输入,那就只能在一个时钟周期里面读取一条数据,没办法同时执行第 1 条指令的读取内存数据和第 4 条指令的读取指令代码。
在这里插入图片描述
把内存拆成两部分的解决方案,在计算机体系结构里叫作哈佛架构(Harvard Architecture),来自哈佛大学设计Mark I 型计算机时候的设计。我们今天使用的 CPU,仍然是冯·诺依曼体系结构的,并没有把内存拆成程序内存和数据内存这两部分。因为如果那样拆的话,对程序指令和数据需要的内存空间,我们就没有办法根据实际的应用去动态分配了。虽然解决了资源冲突的问题,但是也失去了灵活性。
在这里插入图片描述

在流水线产生依赖的时候必须pipeline stall,也就是让依赖的指令执行NOP。

Intel X86每个指令需要的cycle

Intel xeon
在这里插入图片描述

不同架构带来IPC变化:
在这里插入图片描述

Intel 最新的CPU Ice Lake和其上一代的性能对比数据:
在这里插入图片描述

上图最终结果导致了IPC提升了20%,以及整体效率的提升:
在这里插入图片描述

perf 使用

主要是通过采集 PMU(Performance Monitoring Unit – CPU内部提供)数据来做性能监控

sudo perf record -g -a -e skb:kfree_skb //perf 记录丢包调用栈 然后sudo perf script 查看 (网络报文被丢弃时会调用该函数kfree_skb)  
perf record -e 'skb:consume_skb' -ag  //记录网络消耗  
perf probe --add tcp_sendmsg //增加监听probe  perf record -e probe:tcp_sendmsg -aR sleep 1  
sudo perf sched record -- sleep 1 //记录cpu调度的延时  
sudo perf sched latency //查看  

可以通过perf看到cpu的使用情况:

$ sudo perf stat -a -- sleep 10

Performance counter stats for 'system wide':

 239866.330098      task-clock (msec)         #   23.985 CPUs utilized    /10*1000        (100.00%)
        45,709      context-switches          #    0.191 K/sec                    (100.00%)
         1,715      cpu-migrations            #    0.007 K/sec                    (100.00%)
        79,586      page-faults               #    0.332 K/sec
 3,488,525,170      cycles                    #    0.015 GHz                      (83.34%)
 9,708,140,897      stalled-cycles-frontend   #  278.29% /cycles frontend cycles idle     (83.34%)
 9,314,891,615      stalled-cycles-backend    #  267.02% /cycles backend  cycles idle     (66.68%)
 2,292,955,367      instructions              #    0.66  insns per cycle  insn/cycles
                                             #    4.23  stalled cycles per insn stalled-cycles-frontend/insn (83.34%)
   447,584,805      branches                  #    1.866 M/sec                    (83.33%)
     8,470,791      branch-misses             #    1.89% of all branches          (83.33%)

在这里插入图片描述

IPC测试

实际运行的时候增加如下nop到100个以上

void main() {  
    while(1) {  
         __asm__ ("nop\n\t"  
                 "nop\n\t"  
                 "nop");  
    }  
} 

如果同时运行两个如上测试程序,鲲鹏920运行,每个程序的IPC都是3.99

$ perf stat -- ./nop.out  
    failed to read counter branches  

     Performance counter stats for './nop.out':  

           8826.948260      task-clock (msec)         #    1.000 CPUs utilized  
                     8      context-switches          #    0.001 K/sec  
                     0      cpu-migrations            #    0.000 K/sec  
                    37      page-faults               #    0.004 K/sec  
        22,949,862,030      cycles                    #    2.600 GHz  
             2,099,719      stalled-cycles-frontend   #    0.01% frontend cycles idle  
            18,859,839      stalled-cycles-backend    #    0.08% backend  cycles idle  
        91,465,043,922      instructions              #    3.99  insns per cycle  
                                                      \#    0.00  stalled cycles per insn  
       <not supported>      branches  
                33,262      branch-misses             #    0.00% of all branches  

           8.827886000 seconds time elapsed  

intel X86 8260

$ perf stat -- ./nop.out  

     Performance counter stats for './nop.out':  

          65061.160345      task-clock (msec)         #    1.001 CPUs utilized  
                    46      context-switches          #    0.001 K/sec  
                    92      cpu-migrations            #    0.001 K/sec  
                   108      page-faults               #    0.002 K/sec  
       155,659,827,263      cycles                    #    2.393 GHz  
       <not supported>      stalled-cycles-frontend  
       <not supported>      stalled-cycles-backend  
       603,247,401,995      instructions              #    3.88  insns per cycle  
         4,742,051,659      branches                  #   72.886 M/sec  
             1,799,428      branch-misses             #    0.04% of all branches  

          65.012821629 seconds time elapsed  

这两块CPU理论IPC最大值都是4,实际x86离理论值更远一些. 增加while循环中的nop数量(从132增加到432个)IPC能提升到3.92

IPC和超线程

ipc是指每个core的IPC

超线程(Hyper-Threading)原理

概念:一个核还可以进一步分成几个逻辑核,来执行多个控制流程,这样可以进一步提高并行程度,这一技术就叫超线程,有时叫做 simultaneous multi-threading(SMT)。

超线程技术主要的出发点是,当处理器在运行一个线程,执行指令代码时,很多时候处理器并不会使用到全部的计算能力,部分计算能力就会处于空闲状态。而超线程技术就是通过多线程来进一步“压榨”处理器。pipeline进入stalled状态就可以切到其它超线程上

举个例子,如果一个线程运行过程中,必须要等到一些数据加载到缓存中以后才能继续执行,此时 CPU 就可以切换到另一个线程,去执行其他指令,而不用去处于空闲状态,等待当前线程的数据加载完毕。通常,一个传统的处理器在线程之间切换,可能需要几万个时钟周期。而一个具有 HT 超线程技术的处理器只需要 1 个时钟周期。因此就大大减小了线程之间切换的成本,从而最大限度地让处理器满负荷运转。

ARM芯片基本不做超线程,另外请思考为什么有了应用层的多线程切换还需要CPU层面的超线程?

超线程(Hyper-Threading)物理实现: 在CPU内部增加寄存器等硬件设施,但是ALU、译码器等关键单元还是共享。在一个物理 CPU 核心内部,会有双份的 PC 寄存器、指令寄存器乃至条件码寄存器。超线程的目的,是在一个线程 A 的指令,在流水线里停顿的时候,让另外一个线程去执行指令。因为这个时候,CPU 的译码器和 ALU 就空出来了,那么另外一个线程 B,就可以拿来干自己需要的事情。这个线程 B 可没有对于线程 A 里面指令的关联和依赖。

CPU超线程设计过程中会引入5%的硬件,但是有30%的提升(经验值,场景不一样效果不一样,阿里的OB/MySQL/ODPS业务经验是提升35%),这是引入超线程的理论基础。如果是一个core 4个HT的话提升会是 50%

超线程如何查看

如果physical id和core id都一样的话,说明这两个core实际是一个物理core,其中一个是HT。

在这里插入图片描述

physical id对应socket,也就是物理上购买到的一块CPU; core id对应着每个物理CPU里面的一个物理core,同一个phyiscal id下core id一样说明开了HT

IPC和超线程的关系

IPC 和一个core上运行多少个进程没有关系。实际测试将两个运行nop指令的进程绑定到一个core上,IPC不变, 因为IPC就是从core里面取到的,不针对具体进程。但是如果是这两个进程绑定到一个物理core以及对应的超线程core上那么IPC就会减半。如果程序是IO bound(比如需要频繁读写内存)首先IPC远远低于理论值4的,这个时候超线程同时工作的话IPC基本能翻倍
在这里插入图片描述

对应的CPU使用率, 两个进程的CPU使用率是200%,实际产出IPC是2.1+1.64=3.75,比单个进程的IPC为3.92小多了。而单个进程CPU使用率才100%
在这里插入图片描述

以上测试CPU为Intel® Xeon® Platinum 8260 CPU @ 2.40GHz (Thread(s) per core: 2)

主频和性价比

拿Intel 在数据中心计算的大核CPU IvyBridge与当时用于 存储系列的小核CPU Avoton(ATOM), 分别测试阿里巴巴(Oceanbase ,MySQL, ODPS)的workload,得到性能吞吐如下:

Intel 大小CPU 核心                   阿里 Workload Output(QPS)

Avoton(8 cores) 2.4GHZ                 10K on single core

Ivy Bridge(2650 v2 disable HT) 2.6GHZ      20K on single core

Ivy Bridge(2650 v2 enable HT) 2.4GHZ       25K on single core

Ivy Bridge(2650 v2 enable HT) 2.6GHZ       27K on single core
  1. 超线程等于将一个大核CPU 分拆成两个小核,Ivy Bridge的数据显示超线程给 Ivy Bridge 1.35倍(27K/20K) 的提升
  2. 现在我们分别评判 两种CPU对应的性能密度 (performance/core die size) ,该数据越大越好,根据我们的计算和测量发现:Avoton(包含L1D, L1I, and L2 per core)大约是 3~4平方毫米,Ivy Bridge (包含L1D, L1I, L2 )大约是12~13平方毫米, L3/core是 6~7平方毫米, 所以 Ivy Bridge 单核心的芯片面积需要18 ~ 20平方毫米。基于上面的数据我们得到的 Avoton core的性能密度为 2.5 (10K/4sqmm),而Ivy Bridge的性能密度是1.35 (27K/20sqmm),因此相同的芯片面积下 Avoton 的性能是 Ivy Bridge的 1.85倍(2.5/1.35).
  3. 从功耗的角度看性能的提升的对比数据,E5-2650v2(Ivy Bridge) 8core TDP 90w, Avoton 8 core TDP 20瓦, 性能/功耗 Avoton 是 10K QPS/20瓦, Ivy Bridge是 27KQPS/90瓦, 因此 相同的功耗下 Avoton是 Ivy Bridge的 1.75倍(10K QPS/20)/ (27KQPS/95)
  4. 从价格方面再进行比较,E5-2650v2(Ivy Bridge) 8core 官方价格是1107美元, Avoton 8 core官方价格是171美元。性能/价格 Avoton是 10KQPS/171美元,Ivy Bridge 是 27KQPS/1107美元, 因此相同的美元 Avoton的性能是 Ivy Bridge 的**2.3倍(**1 10KQPS/171美元)/ (27KQPS/1107美元)

从以上结论可以看到在数据中心的场景下,由于指令数据相关性较高,同时由于内存访问的延迟更多,因此复杂的CPU体系结构并不能获得相应性能提升,该原因导致我们需要的是更多的小核CPU,以此达到高吞吐量的能力,因此2014年我们向Intel提出需要将物理CPU的超线程由 2个升级到4个/8个, 或者直接将用更多的小核CPU增加服务器的吞吐能力,最新数据表明Intel 会在大核CPU中引入4个超线程,和在相同的芯片面积下引入更多的小核CPU。

预测:为了减少数据中心的功耗,我们需要提升单位面积下的计算密度,因此将来会引入Rack Computing的计算模式,每台服务器将会有4~5百个CPU core,如果使用4个CPU socket,每台机器将会达到~1000个CPU core,结合Compute Express Link (CXL), 一个机架内允许16台服务器情况下,可以引入共享内存,那么一个进程可以运行在上万个CPU core中,这样复杂环境下,我们需要对于这样的软件环境做出更多的布局和优化。

perf top 和 pause 的案例

在Skylake的架构中,将pause由10个时钟周期增加到了140个时钟周期。主要用在spin lock当中因为spin loop多线程竞争差生的内存乱序而引起的性能下降。pause的时钟周期高过了绝大多数的指令cpu cycles,那么当我们利用perf top统计cpu 性能的时候,pause会有什么影响呢?我们可以利用一段小程序来测试一下.

测试机器:
CPU: Intel® Xeon® Platinum 8163 CPU @ 2.50GHz * 2, 共96个超线程

案例:
在这里插入图片描述

对如上两个pause指令以及一个 count++(addq),进行perf top:
在这里插入图片描述

可以看到第一个pasue在perf top中cycles为0,第二个为46.85%,另外一个addq也有48.83%,基本可以猜测perf top在这里数据都往后挪了一个。

问题总结:
我们知道perf top是通过读取PMU的PC寄存器来获取当前执行的指令进而根据汇编的symbol信息获得是执行的哪条指令。所以看起来CPU在执行pause指令的时候,从PMU中看到的PC值指向到了下一条指令,进而导致我们看到的这个现象。通过查阅《Intel® 64 and IA-32 Architectures Optimization Reference Manual》目前还无法得知这是CPU的一个设计缺陷还是PMU的一个bug(需要对pause指令做特殊处理)。不管怎样,这个实验证明了我们统计spin lock的CPU占比还是准确的,不会因为pause指令导致PMU采样出错导致统计信息的整体失真。只是对于指令级的CPU统计,我们能确定的就是它把pause的执行cycles 数统计到了下一条指令。

补充说明: 经过测试,非skylake CPU也同样存在perf top会把pause(执行数cycles是10)的执行cycles数统计到下一条指令的问题,看来这是X86架构都存在的问题。

perf 和火焰图

调用 perf record 采样几秒钟,一般需要加 -g 参数,也就是 call-graph,还需要抓取函数的调用关系。在多核的机器上,还要记得加上 -a 参数,保证获取所有 CPU Core 上的函数运行情况。至于采样数据的多少,在讲解 perf 概念的时候说过,我们可以用 -c 或者 -F 参数来控制。

   83  07/08/19 13:56:26 sudo perf record -ag -p 4759
   84  07/08/19 13:56:50 ls /tmp/
   85  07/08/19 13:57:06 history |tail -16
   86  07/08/19 13:57:20 sudo chmod 777 perf.data
   87  07/08/19 13:57:33 perf script >out.perf
   88  07/08/19 13:59:24 ~/tools/FlameGraph-master/./stackcollapse-perf.pl ~/out.perf >out.folded
   89  07/08/19 14:01:01 ~/tools/FlameGraph-master/flamegraph.pl out.folded > kernel-perf.svg
   90  07/08/19 14:01:07 ls -lh
   91  07/08/19 14:03:33 history

$ sudo perf record -F 99 -a -g -- sleep 60 //-F 99 指采样每秒钟做 99

执行这个命令将生成一个 perf.data 文件:

执行sudo perf report -n可以生成报告的预览。  
执行sudo perf report -n –stdio可以生成一个详细的报告。  
执行sudo perf script可以 dump 出 perf.data 的内容。
# 折叠调用栈
$ FlameGraph/stackcollapse-perf.pl out.perf > out.folded
# 生成火焰图
$ FlameGraph/flamegraph.pl out.folded > out.svg

ECS和perf

在ECS会采集不到 cycles等,cpu-clock、page-faults都是内核中的软事件,cycles/instructions得采集cpu的PMU数据,ECS采集不到这些PMU数据。

在这里插入图片描述

Perf 和 false share cache_line

从4.2kernel开始,perf支持perf c2c (cache 2 cahce) 来监控cache_line的伪共享

系列文章

CPU的制造和概念

CPU 性能和Cache Line

Perf IPC以及CPU性能

Intel、海光、鲲鹏920、飞腾2500 CPU性能对比

飞腾ARM芯片(FT2500)的性能测试

十年后数据库还是不敢拥抱NUMA?

一次海光物理机资源竞争压测的记录

Intel PAUSE指令变化是如何影响自旋锁以及MySQL的性能的

参考资料

perf详解

CPU体系结构

震惊,用了这么多年的 CPU 利用率,其实是错的cpu占用不代表在做事情,可能是stalled,也就是流水线卡顿,但是cpu占用了,实际没事情做。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值