1 Introduction

他们说,“性能至上”。这在十年前是正确的,现在也同样正确。根据[Dom, 2017]的报道,2017年,全球每天创造出2.5个百万亿字节(quintillions)的数据,而根据[Sta, 2018]的预测,这个数字每年增长25%。在我们越来越注重数据的世界中,信息交换的增长促使需要更快的软件和更快的硬件。可以说,数据的增长不仅对计算能力有需求,还对存储和网络系统有需求。
在PC时代[2],开发人员通常是直接在操作系统之上编写程序,可能会使用少量的库。随着世界向云时代转变,软件堆栈变得更加深入和复杂。大多数开发人员工作的堆栈顶层远离硬件。这些附加层将实际的硬件抽象化,从而允许使用新型加速器来应对新兴工作负载。然而,这种演变的负面影响是,现代应用程序的开发人员对其软件运行的实际硬件的亲密程度减少了。
多亏了摩尔定律,软件程序员几十年来一直过得“轻松愉快”。过去有些软件供应商更喜欢等待新一代的硬件来加速他们的应用程序,并没有花费人力资源来改进他们的代码。从图1可以看出,单线程性能增长正在放缓。

 Figure 1: 40 Years of Microprocessor Trend Data. © Image by K. Rupp via karlrupp.net
transistors:
晶体管(transistor)是现代电子设备中最基本的组成部分之一。它是由半导体材料制成的三层或多层结构,可以放大或开关电流。晶体管的出现推动了现代电子技术的发展,而摩尔定律也是基于晶体管技术的发展而提出的。晶体管的大小越小,它们可以承受的电压就越低,因此变得更加节能。随着硅芯片工艺的不断进步,晶体管的尺寸越来越小,数量也越来越多,这使得计算机性能得到了快速提升。
当不再是每一代硬件都提供显著的性能提升[Leiserson等人,2020]时,我们必须开始更加关注我们的代码运行速度。在寻求提高性能的方法时,开发人员不应该依赖于硬件,而应该开始优化他们应用程序的代码。
马克·安德森(Marc Andreessen),美国企业家和投资家曾说过,“今天的软件效率极低;软件程序员再次有机会真正擅长优化。”(摘自a16z Podcast, 2020)。在我在英特尔工作期间,我也经常听到同样的故事:当英特尔的客户在他们的应用程序中遇到缓慢时,他们立即而无意识地开始责怪英特尔的CPU速度慢。但是当英特尔派出一位性能专家与他们合作并帮助他们改善应用程序时,往往可以将其加速5倍甚至10倍。达到高水平的性能很具挑战性,通常需要大量的努力,但希望这本书能够为您提供工具,帮助您实现这一目标。
1.1 Why Do We Still Need Performance Tuning?
现代CPU每年都会有越来越多的核心。截至2019年底,您可以购买一款高端的服务器处理器,其逻辑核心数将超过100个。这非常令人印象深刻,但这并不意味着我们不再需要关注性能。很多时候,应用程序的性能可能不会随着更多的CPU核心而变得更好。典型的通用多线程应用程序的性能并不总是随着我们为任务分配的CPU核心数量呈线性扩展。理解为什么会发生这种情况以及可能的解决办法对产品未来的增长至关重要。不能进行正确的性能分析和调整,会失去很多性能和金钱,并可能导致产品死亡。
根据[Leiserson et al.,2020]所述,在短期内,大多数应用程序的性能提升主要来自软件栈。遗憾的是,应用程序默认情况下并不能获得最佳性能。[Leiserson et al.,2020]还提供了一个很好的例子,说明可以在源代码级别进行性能改进的潜力。在该文章中,列举了对两个4096x4096矩阵相乘的程序进行性能工程的加速效果,总结在表1中。使用多种优化的最终结果是,程序运行速度快了60,000倍以上。提供这个例子的原因并不是针对Python或Java(它们都是很好的编程语言),而是打破一种信仰:即默认情况下软件拥有“足够好”的性能。
表1:对在具有总计60 GB内存的双插槽Intel Xeon E5-2666 v3系统上运行的两个4096x4096矩阵相乘的程序进行性能工程加速的结果。来自[Leiserson et al.,2020]。

 

 以下是阻止系统默认实现最佳性能的一些最重要因素:
1. CPU限制。这很容易引发疑问:“为什么硬件不能解决我们所有的问题?”现代CPU以惊人的速度执行指令,并且每一代都在不断改进。但是,如果用于执行任务的指令不是最优或甚至是冗余的话,它们就无能为力。处理器不能将次优代码神奇地转换为性能更好的代码。例如,如果我们使用BubbleSort算法来实现排序例程,CPU将不会尝试识别它并使用更好的替代方案,例如QuickSort。它将盲目执行它被告知要做的任何事情。
2. 编译器限制。”但难道这不是编译器的任务吗?编译器为什么不能解决我们所有的问题?”的确,现在的编译器非常聪明,但仍然可能生成次优的代码。编译器擅长消除冗余工作,但是当涉及到更复杂的决策,例如函数内联、循环展开等时,它们可能无法生成最佳的代码。例如,对于编译器是否应该始终将函数内联到它的调用位置中,没有二进制的“是”或“否”的答案。它通常取决于许多因素,编译器应该考虑这些因素。通常,编译器依赖于复杂的成本模型和启发式方法,这些模型和方法可能不适用于每种可能的情况。此外,除非编译器确定安全且不影响生成的机器代码的正确性,否则无法执行优化。对于编译器开发人员来说,确保特定的优化在所有可能的情况下都能生成正确的代码可能非常困难,因此他们通常需要保守并避免执行某些优化。最后,编译器通常不会转换程序使用的数据结构,这些数据结构在性能方面也非常重要。
编译器会根据硬件做不同优化么:
是的,编译器可以针对不同的硬件平台进行处理器优化,以实现更好的性能。在编译过程中,编译器会尽可能利用硬件特性来生成更高效的代码。例如,针对不同的CPU架构,编译器可以使用不同的指令集,或者对代码进行向量化操作以支持SIMD指令等。同时,编译器也会通过其他优化手段,如调整内存访问等,使代码适应不同的硬件特征,从而获得更好的性能表现。因此,在编写高性能代码时,需要考虑到目标硬件平台,并结合编译器提供的优化选项,以获得更好的性能。
AI编译器:
AI编译器是使用人工智能技术的编译器,可以通过学习和分析代码、硬件架构和优化策略等数据来生成高效的优化代码。与传统的编译器相比,AI编译器可以更加个性化地优化代码,并且可以快速适应不同的硬件平台和优化方法。AI编译器可以使用机器学习和深度学习技术来进行自我学习和优化,以提高代码的性能表现。AI编译器的出现为软件开发提供了更高效、更准确的优化方案,并且越来越受到广大开发者的重视和青睐。
3 算法复杂度分析的局限性。开发人员经常过于关注算法的复杂度分析,导致他们选择具有最优算法复杂度的流行算法,即使对于给定的问题来说,这种算法可能并不是效率最高的。比如在排序算法中,InsertionSort和QuickSort,后者在平均情况下的Big O表示法上明显胜出:InsertionSort是O(N2),而QuickSort只有O(N log N)。然而,对于相对较小的数据规模N,InsertionSort的实际表现会优于QuickSort。复杂度分析不能考虑到各种算法的分支预测和缓存效果等因素,因此它们只是将这些因素封装在隐含常数C中,这有时会对性能产生重大影响。如果盲目依赖Big O表示法而没有在目标工作负载上进行测试,那么可能会使开发人员走上错误的道路。因此,对于特定问题,最佳算法不一定在所有情况下都是最优的。
上述描述的局限性为我们优化软件性能留下了空间。广义上讲,软件堆栈包含许多层,例如固件、BIOS、操作系统、库以及应用程序源代码。但由于大多数较低层的软件不在我们直接控制之下,因此我们主要关注源代码。另一个我们将会涉及很多的重要软件部分是编译器。通过各种提示,使编译器生成所需的机器代码可以获得令人满意的加速效果。在整本书中,您将发现许多这样的例子。
个人经验:要成功地实现应用程序所需的改进,您不必成为一个编译器专家。根据我的经验,至少90%的所有转换都可以在源代码级别完成,而无需深入挖掘编译器源代码。尽管如此,在性能相关工作中,了解编译器的工作原理以及如何使其执行所需的操作始终是有优势的。
突然想到的:
1080p@60fps camera录像预览 mtk/qcom为何比apple功耗高出一大截子?
iphone的用户画像只是iphone,soc包括里面的camea isp hw pipeline和软件都是自研的,目标客户只是服务于iphone,
hal层偏客制化,hal层的cpu mips预期不会很大。
而mtk/qcom的用户画像不同,他们的用户画像是各个品牌商,目标客户是各个品牌商,
hal层偏非客制化,更注重的是兼容性和扩展性,hal层的cpu mips预期会较大。
目前,vcf vaf目前看起来也是更注重兼容性和扩展性,这对写code来说符合正常思路,但对于功耗性能来说未必是一件好事。
此外,现在,让应用程序通过分布在多个核心上进行扩展是必不可少的,因为单线程性能往往会达到一个瓶颈。这样的启用需要应用程序线程之间的高效通信,消除多线程程序中典型的资源浪费和其他问题。
需要提到的是,性能提升不仅仅来自于对软件的调整。根据[Leiserson等人,2020]的说法,未来潜在速度提升的另外两个主要来源是算法(尤其是针对新的问题域,如机器学习)和简化的硬件设计。算法在应用程序的性能中显然发挥着重要的作用,但我们不会在本书中涉及该主题。我们也不会讨论新的硬件设计主题,因为大多数时候,软件开发人员必须处理现有硬件。然而,了解现代CPU设计对于优化应用程序是很重要的。
“在摩尔定律结束后的时代,使代码运行得更快,特别是将其调整到运行硬件上变得越来越重要。” [Leiserson等人,2020]
本书中的方法论侧重于从应用程序中挤出最后一丝性能。这些转换可以归结为表1的第6和第7行。讨论的改进类型通常不大,并且往往不超过10%。但是不要低估10%速度提升的重要性。对于在云配置中运行的大型分布式应用程序尤其重要。根据[Hennessy,2018]的说法,在2018年,谷歌在实际运行云的计算服务器上花费的钱与其在电力和冷却基础设施上的花费大致相同。能源效率是一个非常重要的问题,通过优化软件可以改善能源效率。
“在这样的规模下,了解性能特征变得至关重要-即使是性能或利用率的微小改进也可能转化为巨大的成本节约。” [Kanev等人,2015]
1.2 Who Needs Performance Tuning?
在高性能计算(HPC)、云服务、高频交易(HFT)、游戏开发和其他对性能要求严格的领域,性能工程无需进行太多的证明就是非常必要的。例如,谷歌报告称,搜索速度慢2%导致每个用户搜索次数减少2%。对于Yahoo!而言,页面加载时间快400毫秒会导致5-9%的流量增加。在大数字的竞赛中,小幅改进可以产生重大影响。这些例子证明了服务运行得越慢,使用它的人就越少。
有趣的是,性能工程不仅在前面提到的领域中需要,现在在通用应用程序和服务领域也需要。如果无法满足性能要求,许多我们每天使用的工具根本不存在。例如,集成到Microsoft Visual Studio IDE中的Visual C++ IntelliSense功能具有非常严格的性能限制。为了使IntelliSense自动完成功能正常工作,它们必须在毫秒级别的时间内解析整个源代码库。
如果需要几秒钟才能建议自动完成选项,就没有人使用源代码编辑器了。这样的功能必须非常响应,并且随着用户输入新代码而提供有效的续写。类似应用程序的成功只能通过考虑性能和周到的性能工程来实现。
有时,快速工具会在其最初设计的领域之外找到用途。例如,现在像虚幻引擎和Unity这样的游戏引擎被用于建筑、3D可视化、电影制作和其他领域。由于它们的性能非常好,因此对于需要2D和3D渲染、物理引擎、碰撞检测、声音、动画等应用程序来说,它们是天然的选择。
“快速工具不仅可以让用户更快地完成任务,还可以以全新的方式完成全新类型的任务。”- Nelson Elhage在他的博客文章中写道(2020年)。
我希望无需明确说明,人们讨厌使用慢速软件。应用程序的性能特征可能是客户转向竞争对手产品的唯一因素。通过强调性能,您可以为产品赢得竞争优势。性能工程是重要且有回报的工作,但可能非常耗时。事实上,性能优化是一个永无止境的游戏。总会有一些可以优化的地方。不可避免地,开发人员将会达到收益递减点,进一步改善会付出非常高的工程成本,可能并不值得努力。从这个角度来看,知道何时停止优化是性能工作的关键方面。一些组织通过将此信息整合到代码审查过程中来实现:源代码行附有相应的“成本”指标。使用这些数据,开发人员可以决定是否值得提高特定代码片段的性能。
在开始性能调整之前,请确保您有足够的理由这么做。仅为了优化而进行优化是没有意义的,如果它不为您的产品增加价值。注意性能工程从明确定义性能目标开始,说明您正在尝试实现什么以及为什么这样做。此外,您应该选择用于衡量是否达到目标的指标。您可以在[Gregg,2013]和[Akinshin,2019]中了解有关设置性能目标的主题的更多信息。
尽管如此,练习和掌握性能分析和调整的技巧总是很棒的。如果您出于这个原因阅读本书,那么欢迎继续阅读。
1.3 What Is Performance Analysis?
如果您曾经与同事辩论某个代码片段的性能问题,那么您可能知道预测哪个代码将最有效有多难。由于现代处理器内部存在如此多的移动部件,即使对代码进行微小的调整也可能触发显着的性能变化。这就是为什么本书中的第一个建议是:始终进行测量。个人经验:我看到很多人在尝试优化其应用程序时依靠直觉。通常,这会导致散乱地修复一些问题,而不对应用程序的性能产生任何真正的影响。
经验不丰富的开发人员通常会在源代码中进行更改,希望它们会改善程序的性能。其中一个例子是将i++替换为++i,假设之前的i值未被使用。在一般情况下,这个变化不会对生成的代码产生任何影响,因为每个体面的优化编译器都会认识到之前使用的i值未被使用,并且将消除重复的副本。许多在世界范围内流传的微观优化技巧过去有效,但当前的编译器已经学会了它们。此外,有些人倾向于过度使用传统的位操作技巧。其中一个例子是使用基于异或的交换惯用语17,而实际上,简单的std::swap可以产生更快的代码。这样的意外更改可能不会改善应用程序的性能。找到正确的修复位置应该是谨慎的性能分析的结果,而不是凭直觉和猜测。
有许多性能分析方法18可能会或可能不会带领您发现问题所在。本书介绍的特定于CPU的性能分析方法有一个共同之处:它们是基于收集有关程序执行方式的某些信息。最终对程序源代码进行的任何更改都是通过分析和解释收集的数据来驱动的。找到性能瓶颈只是工程师工作的一半。第二个部分是适当地修复它。有时在程序源代码中更改一行就可以大大提高性能。性能分析和调整都是关于如何找到并修复这条线。错过这样的机会可能会造成很大的浪费。
1.4 What is discussed in this book?
本书旨在帮助开发人员更好地理解其应用程序的性能,学习发现效率低下的地方并消除它们。为什么我的手写压缩程序比传统的压缩程序慢两倍?为什么我对函数的更改导致性能下降了两倍?客户抱怨我的应用程序很慢,我不知道从哪里开始?我已经将程序优化到了最大潜力吗?我该如何处理所有这些缓存未命中和分支预测错误?希望通过本书结束时,您将对这些问题有所回答。
本书包含以下内容概要:
• 第2章讨论如何进行公正的性能实验并分析其结果。介绍性能测试和比较结果的最佳实践。
• 第3章和第4章介绍了CPU微体系结构和性能分析术语的基础知识;如果您已经掌握了这些知识,可以跳过这部分内容。
• 第5章探讨了几种最流行的性能分析方法。它解释了分析技术的工作原理以及可以收集哪些数据。
• 第6章介绍了现代CPU提供的支持和增强性能分析的功能。它解释了它们的工作原理以及可以解决哪些问题。
• 第7-9章包含了典型性能问题的解决方案。它以最方便与本书中最重要的概念之一 - Top-Down微体系结构分析(见6.1节)一起使用。
• 第10章包含了优化主题,这些主题并不特别与前三章涵盖的任何类别相关,但仍然重要到足以在本书中找到位置。
• 第11章讨论了分析多线程应用程序的技术。概述了优化多线程应用程序性能的一些最重要的挑战以及可用于分析多线程应用程序的工具。这个主题本身相当大,因此本章只集中讨论硬件特定问题,如“假共享”。
本书提供的示例主要基于开源软件:Linux作为操作系统,基于LLVM的Clang编译器用于C和C++语言,以及Linux Perf作为性能分析工具。选择这些技术的原因不仅在于它们的流行,还在于它们的源代码是开放的,这使我们更好地理解它们的工作原理。这对于学习本书中介绍的概念尤其有用。有时,我们还会展示一些专有工具,例如Intel® VTune™Profiler等,这些工具在其领域是“大牌”。
1.5 What is not in this book?
系统性能取决于不同的组件:CPU、操作系统、内存、I/O设备等。应用程序可以通过调整系统的各个组件来获得效益。一般来说,工程师应该对整个系统的性能进行分析。然而,在系统性能中最重要的因素是其核心——CPU。这就是为什么本书主要从CPU的角度着眼于性能分析,偶尔涉及操作系统和内存子系统。本书的范围不超出单个CPU,因此我们不会讨论针对分布式、NUMA和异构系统的优化技术。利用OpenCL和openMP等解决方案将计算异构到加速器(GPU、FPGA等)上也不在本书的讨论范围之内。
本书的核心是Intel x86 CPU架构,不提供针对AMD、ARM或RISC-V芯片的具体调整方法。尽管如此,本书中讨论的许多原则同样适用于这些处理器。此外,本书选择Linux作为操作系统,但对于大多数示例来说并不重要,因为相同的技术有利于在Windows和Mac操作系统上运行的应用程序。
本书中所有的代码片段都是用C、C++或x86汇编语言编写的,但在很大程度上,本书中的思想也可以应用于其他编译成本地代码的语言,如Rust、Go甚至Fortran。由于本书针对运行接近硬件的用户模式应用程序,我们将不讨论托管环境,例如Java。
最后,作者假设读者完全掌握他们开发的软件,包括所选择的库和编译器。因此,本书不是关于调整已购买的商业软件包的内容,例如调整SQL数据库查询。
1.6 Chapter Summary
总的来说,在过去的几年中,硬件单线程性能的提升已经不像以前那样显著了。这就是为什么性能调优变得比过去40年更加重要的原因。当前计算机行业正在发生比自90年代以来任何时候都更大的变化。
根据[Leiserson et al., 2020]的研究结果,软件调优将成为未来性能提升的关键驱动之一。不应低估性能调优的重要性。对于大型分布式应用程序,每个小的性能改进都会带来巨大的成本节约。
软件并没有默认的最佳性能。某些限制存在阻止应用程序达到其完全的性能潜力。硬件和软件环境都具有这样的限制。CPU不能加速缓慢的算法。编译器远未生成每个程序的最佳代码。由于硬件特定性,某个问题的最佳已知算法并不总是最有效的。所有这些都为我们优化应用程序性能留下了空间。
对于某些类型的应用程序,性能不仅仅是一个功能。它使用户以新的方式解决新的问题。
软件优化应该得到强有力的商业需求的支持。开发人员应该设定量化的目标和指标,这些目标和指标必须用于衡量进展。
由于影响现代平台性能的因素有太多,预测某段代码片段的性能几乎是不可能的。在实施软件优化时,开发人员不应该依靠直觉,而是使用仔细的性能分析。
 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值