令人惊讶的速度很慢

2021-04-08 11:37:52

我对性能优化和尽可能使软件ASe足够的亲和力。多年来,我遇到了特定的实例和常用模式,使软件或计算机慢。在这篇文章中,我' ll闪耀着一些聚光灯。

我标题这篇文章令人惊讶的缓慢,因为慢化是我的绝思跋涉或导致缓慢的弱化做法足以让我认为许多程序员对他们的存在感到惊讶。

以下部分在很大程度上是独立的。所以樱桃挑选你感兴趣的人。

构建系统通常在构建阶段之前具有环境检测/配置。在Unix Land中,AutoConf生成的配置普遍存在。 CMake也很受欢迎。这些工具运行一堆codeto探测当前系统的状态,使构建配置不适合当前构建环境。例如,它们' ll探测器需要编译,它的版本和它具有哪些错误和功能。

这种环境检测和配置是必要的邪恶BecaUseMachines,环境通常很大,并且您需要考虑这些差异。

问题是,这种配置步骤通常需要更长时间才能运行Thanthe Build本身!为小程序或库构建系统将以常数为10秒运行配置,并完成在该时间的一小部分中链接的实际编译。换句话说,执行构建的设置需要长时间的构建本身!

根据您拥有的许多CPU核心,差异可能不明显。但是,我有一个16个核心/ 32线程Ryzen 5950x作为我的主PC,并且配置步骤的relivesLowness是痛苦的观察。

我发现更令人震惊的是,即使对于大型项目,配置时间通常仍然是实际构建的时间。我不确定inthis仍然是真的,但几年前观察到建筑物LLVM / Clangon A 96 VCPU EC2实例导致更多时间在CMake / Confighingthan编译和链接中花费更多时间!而且,它是一个非常大的C ++项目,汇编了数千个源文件!

构建配置通常是串行执行大多数人考虑实际构建之前的离散步骤。为了提高效率,需要并行化。即使是更好,它应该是ContentedInto主构建DAG本身,所以构建的一部分可以开始运行,而不需要等待所有构建配置。不幸的是,许多常见的工具性能构建配置可以' t易于适应该模型。所以那里'我们中的许多人都可以做到。

此问题的另一个解决方案正在避免环境中的一个问题。如果您有确定性和可重复的建筑环境,您可以拍摄很多快捷方式来跳过环境检测,只需更多地' T.这或多或少地是博基这样的现代博士工具的方法。我知道工具般的挥之克尔的速度收益是多少,因为消除了环境配置。我怀疑它是很多!

Windows上的新进程可以' t开始像PosixBased操作系统一样快速生成,如Linux。在Windows上,假设一个新的进程果酱10-30ms产卵。在Linux上,新进程(通常通过fork()+ exec()将占用单个数字毫秒以产卵(如果)。

几十毫秒是CPU时间的永恒。它已经足够了,它为人们瞬间达到了大量的时间预算。因此,这可能有助于窗户比Linux慢的感知。

如果您的程序架构由产卵新进程左右(这在Unix Land中常见)组成,这可能会在Windows上构成性能问题,因为Windows上的新进程创建的开销可以真正添加:

使用上面的配置示例,配置文件通常是shellscripts。 Shell脚本通常通过产卵,如Grep,Sed和Sort等其他过程做了很多工作。即使是[运营商是否可以是重新进程(严重:其中)也可能是A / USR / BIN / [在yourposix环境中执行)。 (虽然[可能是shell内置。)commandpipe链条(例如命令| grep | awk)Spawn多个进程Seriveand可以视觉缓慢运行。无论如何,配置千分之一的新进程来产生数千个新进程并不罕见。假设每次进程10毫秒,AT1,000的调用是只有产卵新流程的10多个开销! Thisfurther加剧了上一节中的问题!

如果您的软件在Windows上运行,请考虑相对流程产卵将具有的影响。考虑使用更长寿命的守护程序/后台进程的多线程架构师。

许多年前,我正在分析Mercurial,以帮助提高Windows上的工作指向结账速度,因为用户在Windows上的Checkoutimes比Linux上慢得多,即使在同一台机器上也是如此。

我以为我可以将其粉笔达到NTFS与Linux文件系统或普通内核/ OS级效率差异。我真正学生的令人惊讶。

当我在Windows上开始剖析梳理程序时,我观察到大多数I / OAPI在几十个微秒内完成,也许现在是一个毫秒的两个。 Windows / NTFS性能似乎很棒!

除了CliftHandle()。这些电话通常需要1-10 +毫秒。我似乎奇怪的是文件写道 - 即使是持续的文件Writesthat就足以吹过任何写缓冲能力 - 速度缩短了慢速。如果您使用完成端口(即异步I / O),CloseHandle()速度将更加令人困惑。此行为for Completion端口与MSDN文档所说的内容相反(函数应该立即返回,并且稍后可以检索其状态)。

虽然我当时没有意识到这一点,但这个原因是windowsdefender。 Windows Defender(和其他反病毒/扫描软件)通常通过安装' s称为文件系统filterdriver来工作。这是一个内核驱动程序,基本上将自己挂钩到克尼尔德接收I / O和文件系统事件上的回调。事实证明,CloseFile回调触发写入书面数据。并且此扫描似乎同步发生,阻止返回的CloseHandle()。这个Addsmilliseconds的开销。净效果是WindowsIS上的文件突变I / O由Windows Defender和其他A / V扫描仪急剧减少。

据我所知,只要我可以告诉Windows Defender(并且推测其他A / VScanners)正在运行,并且没有办法使Windows I / O API一致。您可以禁用A / V扫描(以您自己的危险)。但是伪装雇用的诀窍(稍后被生锈的东西仿真)是使用ATHREAD池来调用closeHandle()。即使您在单个线程上执行所有文件和写入I / O并使用后台线程池仅调用CloseHandle(),您可以看到A> 3x加速时间及时写入文件。理想情况下,这种优化应该是由CreateSor在Windows上变为几百个文件的任何软件都使用。这包括Version控制工具,安装程序和归档提取工具。有趣的事实:Rustup可以比开源和商业快速提取/复制工具更快地提取Windows上的Tar文件,因为它采用了这个技巧等等。我相信窗户的Rustup实际上在提取焦油档而不是ISON Linux!

通过扫描软件(如Windows Defenderis)超级讨厌(如Windows Defenderis)超级烦人添加的人工I / O延迟。但是通过使用用于背景的线程池来处理绩效增长通常值得复杂性。如果将这种优化被烘烤成流行的Windows工具(NameelySitallers),我却没有令人疑问,人们会震惊的事情可能会更快。

作为Firefox和#39; S构建系统的维护者,我曾在抱怨基于同龄硬件的同伴慢筹集的人曾担任过少数报道。虽然这是许多原因,但最典型的一个是终端对构建性能的影响。

我学到的是写入大量的输出或将聪明的背包变为终端(例如,写入颜色,移动光标位置写入现有内容)可以急剧减慢应用程序。

通过STDERR / STDOUT写入终端,可能会通过Blockingi / O执行。所以,如果事情处理你的写作()(终端仿真器)并不迅速地处理它的处理,你的进程坐在等待终端。

我们发现不同的终端有自己的怪癖。从历史上看,Windows命令提示符和麦斯科斯队的内置终端为Apply慢慢处理吨输出。我记得(但是可以' t找到firefox的错误orcommit)当我们在某些配置中逐分钟逐个分钟使构建系统安静时。

几年前,NPM陷入困惑的表现吮吸进步旋转器。当我不确定这是终端慢化的大量速度,终端可能播放了一部分的终端,所以算法达到一定程度他们可以接受输入以绘制。

我发现,当我在Firefox和#39; Systsystem中解决这些问题时,现代终端在2012年〜2012年普通普通帖子更好。但是,在终端,如彩色文本,绘画页脚等,我仍然会谨慎行事。始终使用bufferedi / o来最小化实际进入终端的写入(),所需的Flushingas(希望谨慎地)。考虑使用异步线程来编写tostdout / stderr。记录在阻止I / O到STDOUT / STDERR中所花费的总时间,因此YOUCAN测量终端I / O延迟。并定期将墙壁时间ΔTDOUT/ stderr定期与终端连接到终端和/ dev / null,以查看是否有值得关怀的差异。最后,将其贴身写入终端。而不是在输出的每一线路后写一行,考虑缓冲线,以批量发射Alllines以及新页脚。如果绘制该等性质的进度条或旋转器或旋转器,我会将绘制到〜10 Hz,以最小化的开销。

我们希望认为计算机及其处理器是在或off.if只有那么简单的东西。 处理器正在不断改变他们的运营信封,因为它们是竞争的。 以下陈述都是真实的(尽管不是Appliesto所有机器或CPU模型的所有项目): MHz每个CPU核心运行时,可以从1秒到下一个速度波动。 即使其他人正在运行,CPU核心可能会睡眠或进入非常低的电源模式。 如果温度超出阈值,核心可能会显着大幅度。 它们可能拒绝在温度下降之前运行得更快。 有缺陷的传感器可能导致过早行为。 如果其他核心也在运行,核心只能达到最大频率。 其他核心的物理接近可能重要。 它可能需要几十个,数千甚至数千毫秒的怠速核心,以升级到全速。

功率缩放的行为可以根据机器是否连接到外部功率或耗尽电池而变化。

功率缩放的行为可以基本上变化,具体取决于电池是否完全充电或几乎空。

当从左侧充电时,Apple笔记本电脑可能会出现热节流。 (是的,严重:始终从右边向MacBook Pro充电。如果您的员工使用Apple笔记本电脑为CPU繁重的任务,请考虑一项了解鼓励从右侧充电的意识活动。更好,部署软件检查左侧充电和检查左侧充电的软件相应提醒。虽然我尚未找到任何软件或API来检测到这一点。)

现代CPU是真正动态的野兽,他们的经营行为是不可预测的。此外,CPU模型可以从一个到那样不同。例如,EPYC或Xeon处理器可能会在ryzen或核心I7 / I9中表现不同的,这将根据您在桌面或笔记本电脑中运行的情况下不同。 (我观察了几年,吉托Xeon核心赢得了' T Turbo和消费级CPU一样。)

电力波动及其对性能的影响是原因之一,原因是一个非常困难的基准。当前标记时,您需要控制功率变量或至少报告其State,因此结果是适当的。我非常持怀疑态度,即不要报告电力配置/方法(这是最悲伤的是),特别是在笔记本电脑上进行的基准测试,Asbattery操作的设备更容易电力限制Thandesktops或服务器。

我亲自有一个MacBook Pro因为Aninternal螺丝而松开并阻挡了旋转的风扇而变得热压。 Macos Didn' Twarn Me:我所知道的只是我的Firefox Builds越来越慢的诺帕特的理由!我还观察到我的MacBook Pro成为热门的托密侧充电。从右边充电,神奇地使事情变得更快。

在Mozilla,当我们开始向员工推出Xeon Desktops时,我们有报道速度变化速度。在一些操作系统(Mozilla具有非常宽松的中央机器供应和允许的人富裕群体发行的硬件),默认的ACPI C / P-SEED WERESUCH认为CPU核心以不同的方式缩放。

我们观察到的是构建的汇编阶段很好。但有些人报告链接时间2-4x更长的时间(数十秒到几分钟)比等效配置的其他人!这是一个很大的事项,因为增量/非完全构建的WallTime通过链接时间主导。在慢速机器上,DEEVentically发现,CPU核心仅在其潜力的25-50%上运行。思考1.0-1.5 GHz。但是,如果您是额外的CPU繁重的任务,那核心升起。我们发现,除非有足够的CPU负载来说,我们发现了对ACPI C / P-Series的不同默认值为ACPI C / P-Series有不同的默认值。改变到生意电源设置,确保了更好且一致的结果。

笔记本电脑非常容易受到热节流和积极的发电机,以节省电池。我掌握了笔记本电脑的普遍看法,无法实现可靠的性能。鉴于选择,我希望在受控桌面或Serveren环境中运行的繁重工作负载。

但服务器aren' t免疫:他们的acpi c-state和p状态设置甘蓝的影响性能。拨打这些最多可以拨打最多,所以所有的coresrun都是可能的您可以为您提供一些云提供商(如AWS),无需额外的直接费用。然而,更高的能耗对环境不利。数据Centersalready对航空公司行业(非流行时期)的大小有碳足迹,并且占地面积正在增长。因此,请考虑对环境的道德责任,然后让您的Serverfleet消耗可能兆瓦更多的电力。

复杂系统通常将在执行期间执行Python,node.js和其他译员ethandsthousssthousssthouss或更多次。例如,FirefoxBuild系统调用数千个执行常见任务的Python进程,例如包装编译器调用。通过运行HG作为其测试的一部分,通过将HG运行,Mercurial Test Harness Invokesthoussandsandsandsandsands。 I'涉及Node.js,Ruby和其他解释器的类似故事的驾驶员通常在构建系统中使用的上下文中。

忽略关于启动新的解释程序进程的事实是,每个IliNocation通常需要单身才能初始化TheInterpreter。即,新过程在Processexecution开始时花费时间只是进入您告诉它运行的代码。有时,那么过程开销是如此糟糕的是,放缓是显而易见的,并规定了一种技术的使用。 JVM历史上对此令人震惊,这就是为什么使用Java通常需要更少,更长的流程更多,域限制进程。

我之前写的veython'之前写的vealtthon。 2014年,我衡量了Mercurial'测试跑道的总计10-18%只是达到翻译/进程可以运行自定义字节码,占总迟到的30-38%的点数达到梅尔梅尔所做的点命令调度(此处的额外时间主要是模块导入开销)。

您可以认为几毫秒的开销可以' T.如果你乘以1,000,10,000,100,000或更多,毫秒重要:

在Windows上,此问题是复制的,因为它相对较慢的NewProcess启动(见上文)。

程序员需要思考您的进程调用模型。如果可以是问题的话(如果可以是问题的话,则使用更少的进程和/或考虑替代编程符号,如果可以是问题(任何汇总到装配的任何组装通常很好) )。

我对绩效优化的一般亲和力,我的专用性是I / O优化。我认为主要原因是脱离与现代存储设备的潜力和实际内容的潜力如此之大。在纸上,软件应该从现代存储设备获得〜10x,而不是我们通常看到的。

现代存储设备储存快速。我主要的NVME存储可以在> 3 Gb / s(> 6 gb / s顺序)上维持读数,在〜1gb / s(4+ gb / s顺序)上写入,可以执行> 500,000个I / O操作持久性,并且可以在〜10微秒的拉伸率中提供许多I / O操作。现代NVME存储大致符合DDR2DRAM的表现(在2003年推出),在吞吐量方面(延迟仍然是小径,但〜10us没有嘲笑)。

相比之下,几周前只能从我的电脑中逼近的1 TB西部数字鱼子酱黑色纺丝磁盘只能执行〜90 MB / s的顺序读数,1-2 MB / s随机读写,〜12 ms访问时间。 I' M Undure Iops是,但考虑到〜12毫秒的访问时间和纺丝磁盘的物质,它可以' t不到几百。

现代NVME存储比十多年前的最佳开支磁盘快1.5-3个幅度。那么为什么为什么ISN' T所有存储器/ o〜瞬间?

简短的答案是,大多数软件都无法利用现代存储设备的潜在势利,甚至更糟糕地积极地破坏它通过Bad实践。

对于前者,我' ll将您推荐给优秀的现代仓库很快。 它是Bad.TL的API;博士您可以利用现代存储设备的全部功率绕过标准OS /内核I / O基元并直接对设备发出I / Ooperations。 因此,软件抽象Inthe OS / Kernel正在吃很多潜力。 对于软件破坏存储设备电位方面,I' LL短暂的fsync()posix函数。 通过调用此函数,您可以确保将此文件描述符的状态持续到StoragedEvice或者我不想丢失任何改变。 数据一致性和耐用性很重要。 但是实现的成本可能是荒谬的。 事实证明,在实践中,它也在巧妙地难以做到。 I' ll将您推荐给Dan Luu'很好的文件很难。 Papersinedof. ......