注:本文是参照了一些其他文章,原文地址点击这里。
前言
推荐看原文,因为我是懵逼的。并且这也算是广告贴了。
谈到优化应用程序的性能,开发人员不必是性能专家,但应该精通自己的应用程序。许多方面的因素都会对应用程序性能产生影响,需要从硬件平台,代码设计和利用microarchitecture特性对功能代码进行微调。应用程序开发人员了解应用程序如何利用硬件资源的基本要求是,对代码设计和实现有深入理解。这可以通过对硬件microarchitecture有更深入的了解,或者通过使用诸如VTune™ Amplifier的专用分析工具来实现。
top-down方法
一个比较好的性能调优方法是top-down的方式。 这种方法有三个阶段:顶部的系统调整,中间的应用程序调整和底层的microarchitecture调整。 系统调整涉及硬件和操作系统调整,而应用程序调整包括更好的设计,并行化和提高库的效率。 microarchitecture是最后一个阶段,包括仔细选择编译器标志,向量化和代码重构w.r.t内存/缓存优化以及对CPU陷阱的理解,如下图所示。
Microarchitecture和Micro-operations
上图描述了第四代Intel® Core™ microarchitecture,其中上绿色部分是前端,下蓝色部分是pipeline的后端。前端是指令(例如ADD,MUL)被取出和解码(转换为较小的操作,称为micro-operations[μ-Ops])的地方。后端是执行所需操作,实际计算的地方。pipeline的每个块的详细描述超出了本文的范围。下图描述了前端和后端处理器流水线的职责。
micro-operations(也称为μOps)成功完成整个datapath,称为retired。后端可以在每个cycle达到4个μOps。μOps通过(pass through)处理器pipeline并retired;偶尔由于各种分支错误预测,推测性地提取的几个μOps就会在其间被取消。pipeline slot表示处理一个μOp所需的硬件资源,这意味着在每个时钟周期的CPU核上有四个pipeline slot可用。如前所述,每个cycle可以有四个μOps retired,这意味着每个指令在理论上应该耗费0.25个cycle。
在给定的时间点,pipeline slot可以为空或用μOps填充。pipeline slot分为四类,如下图所示。有关pipeline slot分类的更多信息,请参见本文。
VTune™ Amplifier
VTune Amplifier通过利用作为CPU内核的部分PMU(performance monitoring units)来收集测量结果。专用监视计数器可以收集和公开关于硬件资源消耗的信息。在PMU的帮助下,可以测量和检索关于所处理的指令的效率和缓存使用的度量。熟悉各种可用的指标(例如retired instructions; clock ticks; L2, L3 cache statistics; branch mis-predicts等)非常重要。此外,可以测量非CPU的PMU度量,如从/向存储器控制器读/写的字节,以及由Intel® QPI(Intel® QuickPath Interconnect)传输的数据流量。
下面是用于计算不同pipeline slot度量的简要描述和公式。如果后端准备好接受更多μOps,而前端每个周期传送小于4μOps,那么应用程序考虑是front-end bound。这可能是由于提取代码(cache/ITLB问题)或解码指令的延迟所致。front-end bound pipeline slot可以被分类为子类别,如下图所示。
公式:IDQ_UOPS_NOT_DELIVERED.CORE / (4 * Clockticks)
如果由于在pipeline的后端缺少所需的资源而没有传送μOps,则应用程序考虑是back-end bound。这可能是因为内部结构填充了等待数据的μOps。下图中进一步描绘了front-end bound子类别。
公式:Formula: 1 - (Front-end Bound + Bad speculation + Retiring)
由于从分支错误预测或机器清除中恢复,μOps从不retire或分配slots被浪费时,pipeline slot被归类为bad speculation。
公式:(UOPS_ISSUED.ANY - UOPS_RETIRED.RETIRE_SLOTS + 4* INT_MISC.RECOVERY_CYCLES) / (4* Clockticks)
如果成功传递的μOps最终retired,那pipeline slot被归类为retiring
公式:UOPS_RETIRED.RETIRE_SLOTS / (4 * Clockticks)
每个子类别的详细描述超出了本文的范围。 有关每个子类别的深入描述,请参考CPU指标参考。
OVS-DPDK瓶颈分析
通过上面的一堆看不懂的东东,终于可以到了我想看到的性能瓶颈分析了,接着往下看吧,骚年。
流表的报文批处理
OVS-DPDK报文pipeline的一个重要阶段是流量批处理。首先对每个传入的报文进行分类,然后根据其匹配流进行分组,如下图所示。
进入流表批处理的报文,根据该流表定义的相应的action(drop,push / pop VLAN)进行处理。为了提高报文转发性能,同一流表的报文将被批处理。偶尔,批处理中可能会有很少的报文。在最坏的情况下,每个获取的报文都匹配不同的流,因此每个批处理将包含一个数据包。当流表的相应action是将报文转发到某个物理端口时,由于DPDK接口上的数据包传输会导致昂贵的内存映射I/O(MMIO)写操作,所以传输报文可能非常低效。
下图显示了报文处理关系调用图。对于每个输入的报文,在EMC中执行查找以匹配流表。在EMC命中的情况下,将报文使用dp_netdev_queue_batches()
匹配流(请参阅OvS-DPDK源代码中的struct packet_batch_per_flow
)。此后,使用packet_batch_per_flow_execute进行更快的数据包处理。如果流表的相应action是将数据包转发到DPDK端口,netdev_send
将被调用,如下图所示。
64字节UDP报文的性能基准测试
用IXIA流量仪搭建测试环境,它发送64字节的UDP报文。在"PHY2PHY"测试用例中,有数十个不同的流时观察到显着的性能下降。请注意,流规则是唯一的,并且根据报文的源IP匹配流表。示例流程规则如下所示; 这将创建四个批次,并且数据包排队到相应的批次。
$ ovs-ofctl add-flow br0 in_port=4,dl_type=0x0800,nw_src=2.2.2.1,actions=output:2
$ ovs-ofctl add-flow br0 in_port=4,dl_type=0x0800,nw_src=4.4.4.1,actions=output:2
$ ovs-ofctl add-flow br0 in_port=4,dl_type=0x0800,nw_src=6.6.6.1,actions=output:2
$ ovs-ofctl add-flow br0 in_port=4,dl_type=0x0800,nw_src=8.8.8.1,actions=output:2
根据上述流程规则,VTune General Exploration分析显示了对OVS-DPDK传输瓶颈的有趣见解。
VTune Amplifier总结
当VTune分析运行60秒时,下图显示了VTune Amplifier关于pipeline slots占用情况的总结。请注意,粉红色的高亮slots需要注意,并由VTune根据应用程序类别的默认阈值自动高亮显示。
根据VTune文档,下图显示了通过VTune放大器输出的OVS-DPDK结果,HPC(high-performance computing)应用程序中pipeline slots的预期范围之间的比较。
如上图所示,对于特定的测试用例,OVS-DPDK是back-end bound的,retiring(只有35%)远远低于预期和健康70%限制的指令。bottom-up的分析显示了一些有趣的细节,如下图所示。
netdev_dpdk_eth_send()
消耗了总周期的17%。- 上述功能的每个指令周期(CPI)为4.921,远高于理论极限0.25,在HPC应用情况下可接受的范围为1.0。
- 此功能完全是back-end bound,并且几乎不retiring任何指令(小于6%)。
通过扩展back-end Bound列,可以更详细地了解back-end pipeline中发生的情况。 下图描述了back-end pipeline中的瓶颈在何处,并且它指向了L1 cache中的一个重大问题。
列出的函数是关于报文传输的。这可能意味着back-end pipeline在I/O操作中遇到长时间的延迟。所以瓶颈可能是在于MMIO操作。当处理的报文(最多32个)匹配许多不同的流表时,情况尤其如此。在每个报文正在触发不同流表的最坏情况下,将触发MMIO操作,以从相应批处理去处理每个报文。
降低MMIO成本的解决方案
在实际情况下,物理端口的报文可能会遇到很多流表,每个流表的批处理缓存非常少的报文。当报文通过DPDK接口传输时,这变得非常低效。为了摊销MMIO写入的成本,可以使用中间队列来排队"NETDEV_MAX_BURST"(即32个)报文,并以rte_eth_tx_burst
的格式发送数据包。中间队列是使用netdev_dpdk_eth_tx_queue()
函数实现的,它将对数据包进行排队。当满足以下条件之一时,数据包将被传送。
- 如果txq(txq-> count)> = NETDEV_MAX_BURST中的数据包计数,则调用
netdev_dpdk_eth_tx_burst()
来突发报文。 - 超时后,任何等待队列的数据包都必须刷新,而不管其数量如何。
通过这种解决方案,可以观察到back-end和Retiring类别的改进,如下图所示。
下图显示,在应用补丁后,函数netdev_dpdk_eth_send()
从列表的顶部到底部大幅度移动。通过减轻MMIO延迟的影响,测量的CPI从4.921下降到1.108。
请注意,通过报文传输路径中的更多优化可以进一步提高性能; 但是,这超出了本文的范围。以上是使用VTune Amplifier了解和修复HPC应用程序瓶颈的示例。 在我们使用VTune Amplifier的分析中,我们使用了General Exploration模式,并集中在back-end bound度量上。值得注意的是,其他分析类型在VTune Amplifier工具中可用,如热点,高级热点模式,hpc性能,内存访问和锁定和等待。此外,其他指标可用于显示资源消耗。