如何在Ruby中修复速度慢的代码

2020-05-15 01:39:14

Jay Lim和Gannon McGibbonat Shopify,我们相信高度一致、松散耦合的团队可以帮助我们快速行动。由于我们有许多团队独立开发大型单块Rails应用程序,代码效率低下有时会不经意间添加到我们的代码库中。随着时间的推移,这些问题会导致严重的性能倒退。当这种性能倒退明显时,追踪违规提交可能已经为时已晚。这对代码库来说可能是非常具有挑战性的,因为每天都要提交数千个更改。我们如何有效地找出应用程序运行缓慢的原因呢?即使我们已经修复了速度较慢的代码,我们如何才能证明我们的新代码更快呢?这一切都始于性能分析和基准测试。去年,我们写了关于用Ruby on rails编写快速代码的文章。知道如何编写快速代码是有用的,但如果不知道如何修复速度较慢的代码,则是不够的。让我们讨论一下我们可以用来查找、修复慢代码的方法,并证明我们的新解决方案更快。我们还将探索一些案例研究,这些案例研究以使用性能分析和基准测试的真实示例为特色。性能分析在我们开始修复性能不佳的代码之前,我们需要首先找到它。在大型代码库中,识别导致性能瓶颈的代码可能具有挑战性。分析帮助我们很容易做到这一点。什么是分析?分析是一种程序分析类型,它在运行时收集有关程序的度量,如方法调用的频率和持续时间。它是使用一种称为分析器的工具执行的,分析器的输出可以通过各种方式可视化。例如,平面配置文件、调用图和火焰图。为什么要分析我的代码?仅通过查看代码(静态分析、代码审查等)来检测某些问题很有挑战性。剖析的主要目标之一是可观察性。通过了解运行时幕后发生的情况,我们可以更好地理解程序在做什么,并解释应用程序运行缓慢的原因。分析帮助我们将性能瓶颈的范围缩小到特定区域。我如何分析?在我们确定要分析什么之前,我们需要首先弄清楚我们想知道的是:我们是要测量特定代码块的运行时间,还是要测量该代码块中的对象分配?就粒度而言,我们是否需要代码块中每个方法调用所用的时间,或者我们只需要聚合值?这里的运行时间可以进一步细分为CPU时间或挂起时间,要测量运行时间,一个简单的解决方案是测量特定代码块的开始时间和结束时间,并报告差异。如果我们需要更高的粒度,我们对每个方法都这样做。为此,我们使用Ruby中的TracePoint API来挂钩Ruby进行的每个方法调用。类似地,对于对象分配,我们使用ObjectSpace分析器模块来跟踪对象分配,甚至转储Ruby堆以观察其内容。但是,我们可以使用现有的一个可用的分析器,而不是构建定制的分析器解决方案,每个分析器都有自己的优点和缺点。这里有几个选项:1.rbspyrbspy示例随时间推移来自Ruby进程的堆栈框架。它的主要优点是可以作为独立的程序使用,而不需要任何检测代码。一旦我们知道了要评测的Ruby进程标识符(PID),我们就像这样启动评测会话:rbspy record-PID$PID2。与rbspy类似,stackprof示例随着时间的推移堆叠帧,但是来自一段插入指令的Ruby代码。Stackprof用作自定义代码块的分析解决方案:profile=StackProf.run(mode::cpu)do#Code to profileend3。机架-迷你Profiler机架-迷你Profiler GEM是一款功能齐全的基于机架的应用程序评测解决方案。与本节中描述的其他分析器不同,除了调用堆栈采样之外,它还包括一个内存分析器。内存分析器收集诸如垃圾收集(GC)统计数据、分配数量等数据。在幕后,它使用stackprof和memory_profiler。4.。app_profilerapp_profiler是Rack-mini-Profiler的轻量级替代方案。它包含一个仅限机架的中间件,支持Web请求的调用堆栈分析。除此之外,块级分析也可用于任何Ruby应用程序。这些配置文件可存储在可配置存储后端(如Google云存储)中,并可通过可配置查看器(如基于浏览器的火焰图查看器Speedscope)进行可视化。在Shopify,我们收集生产环境中的性能配置文件。Rack Mini Profiler是一个很棒的工具,但是它有很多额外的功能,比如数据库和内存分析,对于我们的用例来说,它似乎太重了。因此,我们构建了App Profiler,它在幕后类似地使用Stackprof。目前,此GEM用于支持我们的按需远程评测I