JavaScript 微观性能测试、历史和局限性

javascript 微观性能测试、历史和局限性

我认为性能优化对许多开发人员感兴趣,因为他们更多地了解完成任务的不同方法。一些内心的声音问道:“哪种方式最好?”虽然“最佳”指标有很多变化,例如 douglas crockford 的 2008 年 javascript:the good parts,但性能是容易获得的,因为我们可以自己测试它。

然而,测试和证明性能并不总是那么容易做到。

一点历史

浏览器大战

到 2000 年代初期,internet explorer 赢得了第一次浏览器战争。 ie 甚至一度成为 mac 上的默认浏览器。曾经占据主导地位的网景公司被出售给美国在线并最终关闭。他们的衍生产品 mozilla 对其新的独立浏览器 phoenix firebird firefox 进行了长达数年的测试。

2003 年,opera 7 推出了 presto,这是一种新的、更快的渲染引擎。 apple 还发布了 safari,这是一款基于鲜为人知的 konqueror khtml 引擎构建的 mac 性能浏览器。 firefox于2004年正式推出。微软于2006年发布了ie 7,opera 9发布了更快的javascript引擎。 2007 年,safari 出现在 windows 和新 iphone 上。 2008 年出现了 google chrome android 浏览器。

随着更多的浏览器和更多的平台,性能成为这一时期的关键部分。新的浏览器版本定期宣布它们是新的最快的浏览器。 apple 的 sunspider 和 mozilla 的 kraken 等基准测试经常在版本中被引用,而 google 则维护着自己的 octane 测试套件。 2010年chrome团队甚至做了一系列“速度测试”实验来展示浏览器的性能。

高性能 javascript

微性能测试在 2010 年代受到了广泛关注。 web 正在从有限的页面交互性转向完整的客户端单页应用程序。 nicholas zakas 的 2010 年高性能 javascript 等书籍展示了看似微小的设计选择和编码实践如何能够产生有意义的性能影响。

不断变化

不久前,javascript 引擎竞赛就致力于解决高性能 javascript 中的一些关键性能问题,引擎的快速变化使得很难知道什么是最好的现在。随着新的浏览器版本和移动设备的出现,微性能测试成为一个热门话题。到 2015 年,现已关闭的性能测试网站 jsperf.com 非常受欢迎,它开始因垃圾邮件而出现性能问题。

测试正确的事情

随着 javascript 引擎的发展,编写测试很容易,但很难确保你的测试公平甚至有效。如果您的测试消耗了大量内存,则后续测试可能会看到垃圾收集造成的延迟。设置时间是否被计入或排除在所有测试之外?测试是否产生相同的输出?测试的背景重要吗?如果我们测试 !~arr.indexof(val) 与 arr.indexof(val) === -1 ,如果我们只是运行表达式或在 if 条件下使用它,会有什么不同吗?

编译器优化

随着脚本解释器被各种编译器取代,我们开始看到编译代码的一些好处和副作用:优化。例如,在没有副作用的循环中运行的代码可能会被完全优化。

// Testing the speed of different comparison operators
for (let i = 0; i 



<p>因为这是执行 10000 次操作而没有输出或副作用,所以优化可能会完全丢弃它。但这并不能保证。</p>

<h3>
  
  
  移动目标
</h3>

<p>此外,微优化在不同版本之间可能会发生显着变化。 jsperf.com 的不幸关闭意味着不同浏览器版本的数百万历史测试比较丢失,但这仍然是我们今天可以看到的。</p>

<p>重要的是要记住,微优化性能测试有很多注意事项。 </p>

<p>随着性能改进开始趋于平稳,我们看到测试结果出现反弹。其中一部分是引擎的改进,但我们也看到引擎针对<em>常见模式</em>优化代码。即使存在更好的编码解决方案,优化通用代码模式对用户来说也有真正的好处,而不是期望每个站点都进行更改。</p>

<h3>
  
  
  变化的景观
</h3>

<p>比不断变化的浏览器性能更糟糕的是,2018 年计时器的准确性和精度发生了变化,以减轻 spectre 和 meltdown 等推测执行攻击。如果您感兴趣的话,我写了一篇关于这些时间问题的单独文章。</p>

<h3>
  
  
  分焦
</h3>

<p>让事情变得复杂的是,您是否针对最新的浏览器或项目支持的最低浏览器进行测试和优化?同样,随着智能手机的普及,处理能力明显较低的手持设备成为重要的考虑因素。知道在哪里分配时间以获得最佳结果或<em>最有影响力</em>结果变得更加困难。 </p>

<h2>
  
  
  过早优化?
</h2>

<blockquote>
过早的优化是万恶之源。
 ——唐纳德·高德纳
</blockquote>

<p>这句话经常被引用。人们用它来暗示,每当我们考虑优化时,我们可能会为了虚幻的或微不足道的收益而浪费时间并使代码变得更糟。在很多情况下这可能是正确的。但这句话还有更多内容:</p>

<blockquote>
<p>我们应该忘记小的效率,大约 97% 的情况下:过早的优化是万恶之源。但我们不应该放弃这关键的 3% 的机会。</p>
</blockquote>

<p>更完整的引用添加了关键上下文。如果我们允许自己这样做,我们可以在<em>小效率</em>上花费大量时间。这通常会花费时间来实现项目目标,但无法提供太多价值。</p>

<h3>
  
  
  收益递减
</h3>

<p>我个人在这些优化上花了很多时间,目前看来并不算浪费。但回想起来,并不清楚这些工作有多少是值得的。我确信我当时写的一些代码将执行时间缩短了几毫秒,但我真的不能说节省的时间是否<em>重要</em>。</p>

<p>google 甚至谈到了 2017 年 octane 测试套件退役的回报递减。我强烈建议您阅读这篇文章,深入了解致力于这项工作的团队在性能优化方面遇到的限制和问题。</p>

<p>那么我们如何聚焦那“关键的3%”呢?</p>

<h2>
  
  
  应用而非操作
</h2>

<p>了解代码的使用方式和时间有助于我们更好地决定关注点。</p>

<h3>
  
  
  工具而非规则
</h3>

<p>不久之后,新浏览器的性能提升和变化就开始将我们从这些类型的微观测试推向更广泛的工具,例如火焰图。<br>
如果您有 30 分钟时间,我推荐这个关于 v8 引擎的 2015 chrome devsummit 演示。它讨论的正是这些问题......浏览器不断变化,跟上这些细节可能很困难。</p>

<p>对正在运行的应用程序进行性能监控和分析可以帮助您快速识别代码的哪些部分运行缓慢或运行频繁。这使您能够处于有利的位置来考虑优化。</p>

<h3>
  
  
  重点
</h3>

<p>使用性能监控工具和库可以让您了解代码如何运行以及哪些部分需要工作。它们还让我们有机会了解不同的领域是否需要在不同的平台或浏览器上工作。也许 localstorage 在内存和 emmc 存储有限的 chromebook 上要慢得多。也许您需要缓存更多信息来应对缓慢或不稳定的蜂窝服务。我们可以猜测哪里出了问题,但测量是更好的解决方案。</p>

<p>如果您的客户群足够大,您可能会发现真实用户监控 (rum) 工具的好处,它可以让您了解实际的客户体验。这些超出了本文的范围,但我已经在几家公司使用它们来了解客户体验的范围,并将重点放在实际性能和错误处理上。</p>

<h2>
  
  
  替代方案
</h2>

<p>深入思考“我如何改进这件事”很容易,但这并不总是最好的答案。您可以退后一步并询问“这是解决此问题的正确解决方案吗?”来节省大量时间</p>

<p>在 dom 上加载非常大的元素列表时出现问题?也许仅在页面上加载可见元素的虚拟化列表可以解决性能问题。</p>

<p>在客户端执行许多复杂的操作?在服务器上计算部分或全部这些会更快吗?部分工作可以缓存吗?</p>

<p>退一步:这是执行此任务的正确用户界面吗?如果您设计的下拉列表预计有 20 个条目,而现在有 3000 个条目,那么您可能需要不同的组件或体验来进行选择。</p>

<h2>
  
  
  够好了吗?
</h2>

<p>对于任何表演作品,都存在一个次要问题:“什么才足够”?有一个来自 stand-up maths 的 matt parker 的精彩视频,讲述了他编写的一些代码以及他的社区如何将其从运行时间<em>周</em>改进到<em>毫秒</em>。虽然令人难以置信的是,这样的优化是可能的,但几乎所有项目都有一个达到“足够好”的点。 </p>

<p>对于只运行一次的程序,几周可能是可以接受的,几个小时会更好,但你快速花费多少时间就成为一个重要的考虑因素。</p>

<p>您可能会认为它就像工程中的<em>公差</em>。我们有一个目标,我们有一个接受范围。我们可以追求完美,同时明白成功和完美并不相同。</p>

<h3>
  
  
  确定绩效目标
</h3>

<p>目标是优化的关键部分。如果你只知道当前状态不好,“让它变得更好”就是一个开放式目标。如果没有优化之旅的目标,当您可以处理更重要的事情时,您可能会浪费时间尝试寻找更高的性能或更多的优化。</p>

<p>我对此没有一个好的衡量标准,因为性能优化可能会有很大差异,但尽量不要迷失在杂草中。这实际上与项目管理和规划有关,而不仅仅是编码解决方案,但在定义优化目标时,开发人员的输入非常重要。正如“替代方案”部分中所建议的,解决方案可能不是“使其更快”。</p>

<h4>
  
  
  设定限制
</h4>

<p>就马特·帕克的情况而言,他<em>最终需要答案</em>,并且不需要将该设备用于其他任何用途。在我们的世界中,我们经常衡量<em>访问者表现</em>及其<em>可能的财务影响</em>与<em>开发人员/团队时间</em>以及您的<em>机会成本</em>,所以措施没那么简单。</p>

<p>假设我们<em>知道</em>将添加到购物车的时间减少 50% 将使我们的收入增加 10%,但完成这项工作需要两个月的时间。有什么比两个月的优化工作产生更大的财务影响吗?你能在更短的时间内实现一些效益吗?再说一次,这是关于项目管理而不是代码。</p>

<h3>
  
  
  隔离复杂性
</h3>

<p>当您确实发现自己需要优化代码时,也是看看是否可以将该代码与项目的其他部分分开的好时机。如果您知道必须编写复杂的优化,这将使代码难以理解,那么将其提取到实用程序或库可以使其更容易重用,并允许您在需要随时间变化时在一个地方更新该优化。</p>

<h2>
  
  
  结论
</h2>

<p>表演是一个复杂的话题,有很多曲折。如果你不小心的话,你可能会投入大量的精力却收效甚微。好奇心可以是一位好老师,但它并不总是能取得成果。玩弄代码性能是有好处的,但也有时间分析项目中缓慢的实际来源并使用可用的工具来帮助解决它们。</p>

<h2>
  
  
  资源
</h2>

  • addy osmani - 使用 devtools flame charts 可视化 js 处理随时间的变化
  • 站立数学 - 有人将我的代码改进了 40,832,277,770%
  • 使用 microsoft copilot 制作的标题图像

以上就是JavaScript 微观性能测试、历史和局限性的详细内容,更多请关注其它相关文章!