在进行TDD时的性能测试最佳实践是什么?

11

我正在开发一个项目,需要进行性能优化。

如何编写一个测试,如果我的优化没有提高程序的速度,测试将失败?

具体来说:

问题不在于发现哪些部分需要优化。我可以使用各种性能剖析和基准测试工具来解决这个问题。

问题在于如何使用自动化测试来记录特定优化确实产生了预期的效果。如果我能使用测试套件在以后发现可能的性能回归,那将非常理想。

我想,我可以运行我的性能剖析工具以获得一些值,然后断言我的优化代码生成更好的值。然而,明显的问题是基准测试的值不是硬性数值,会随着本地环境的变化而变化。

所以,答案是总是使用同一台机器来进行这种集成测试吗?如果是这样,您仍然需要允许结果有一定的模糊性,因为即使在相同的硬件上,基准测试结果也可能不同。那么如何考虑这一点呢?

或者,也许答案是保留旧版本的程序,并比较前后的结果?这是我首选的方法,因为它大多数情况下不受环境影响。有没有人用过这种方法?我想,如果所有测试都可以通过,那么只需要保留一个旧版本,如果最新版本的性能至少与旧版本相同。

9个回答

5
我认为将TDD应用于驱动性能是一个错误。可以使用它来获得良好的设计和可工作的代码,并使用在TDD过程中编写的测试来确保持续的正确性 - 但是一旦你拥有了良好的代码和可靠的测试套件,你就可以进行调优,并且不同于TDD的技术和工具适用。
TDD可以给你带来良好的设计、可靠的代码和测试覆盖率安全网。这使你处于一个良好的调优位置,但是我认为由于你和其他人所提到的问题,它简单地不能让你在调优道路上走得更远。我说这话是作为TDD的忠实粉丝和支持者以及实践者。

1
+1 同意。TDD 确保当您调整系统时,不会破坏其功能。 - Matthew Farwell
1
如果它提到了任何更合适的技术,这个答案会更有帮助。 - bukzor
@bukzor,原帖作者似乎对性能分析的概念和工具有很好的掌握,问题是如何将TDD应用于此。我的答案是这可能不明智;关于如何调整代码性能,还有很多其他问题和答案可供参考。 - Carl Manaster

3

首先,您需要建立一些可接受性能的标准,然后需要设计一个测试,以便在使用现有代码时无法通过这些标准,然后需要调整您的代码以提高性能,直到它通过测试。您可能会有多个性能标准,而且您肯定应该有多个测试。


您是否将此断言提交到版本控制?您是否希望它在多种类型的机器上被检出?如果是(正如我所想象的那样),那么您如何提供任何合理的期望,以确保您的断言在速度较慢或较快的计算机上都是适当的? - bukzor

3
在许多服务器应用程序(可能不适用于您的情况)中,性能问题仅在并发访问和负载下显现。测量例程执行的绝对时间并尝试改进它因此并不是非常有帮助。即使在单线程应用程序中,这种方法也存在问题。测量绝对例程时间依赖于平台提供的时钟,并且这些时钟并不总是非常精确; 您最好依赖于例程所需的平均时间。
我的建议是:
  • 使用分析工具来识别执行次数最多且花费最长时间的例程。
  • 使用 JMeterGrinder 等工具来制定代表性的测试用例、模拟并发访问、将您的应用程序置于压力下并测量(更重要的是)吞吐量和平均响应时间。这将为您提供一个更好的从外部视角看待应用程序行为的想法。
虽然您可以使用单元测试来确定应用程序的某些非功能方面,但我认为上述方法在优化过程中将获得更好的结果。当在单元测试中放置与时间相关的断言时,您将不得不选择一些非常近似的值:时间可能会因您用于运行单元测试的环境而异。 您不希望测试失败,仅因为您的一些同事正在使用较低级别的硬件。
调整是找到正确调整的方法。您已经有一个运行良好的代码,因此在没有建立关键代码部分的情况下事后放置性能相关断言可能会导致您浪费大量时间在优化应用程序中非必要的部分。

时间可能会因为你运行单元测试的环境而有所不同。你不希望测试失败,仅仅因为你的一些同事使用了较差的硬件设备。这完全正确,也是导致我的头痛的部分原因。 - KaptajnKold
在你调整好系统后,如何防止它发生回归?在其他所有软件回归场景中,解决方案是向测试套件中添加断言。 - bukzor

2

记录当前代码的运行时间。

if (newCode.RunningTime >= oldCode.RunningTime) Fail

1
即使在最好的系统中,这种方法会50%的时间产生错误的结果(当一切实际上都很好时却失败了),这是一个无法使用的解决方案。 - bukzor

1

在CI服务器中运行测试和分析。您还可以定期运行负载测试。

您关心差异(如您所述),因此不是要定义绝对值。有一个额外的步骤,将此次运行的性能测量与上一次构建的测量进行比较,并报告%上的差异。如果时间变化重要,则可以引发红旗。

如果您关注性能,则应该明确要达到的目标并加以确认。您应该通过对整个系统进行测试来衡量这些目标。即使您的应用程序逻辑很快,您也可能遇到视图问题而错过目标。您还可以将其与差异方法相结合,但对于这些情况,您对时间变化的容忍度会更低。

请注意,您可以在开发计算机上运行相同的进程,只需使用该计算机中先前的运行而不是开发人员之间共享的计算机。


0

虽然我基本上同意Carl Manaster的回答,但是现代工具使得将TDD提供的一些功能测试优势转化为性能测试成为可能。

在大多数现代性能测试框架中(我的大部分经验都是与Gatling相关的,但我相信大多数性能测试框架的新版本也是如此),可以将自动化性能测试集成到持续集成构建中,并配置它以便在未满足性能要求时CI构建将失败。

因此,只要事先达成一致,确定您的性能要求是可能的(对于某些应用程序可能是由用户或客户协商的SLA驱动),这可以为您提供快速反馈,如果更改创建了性能问题,则可以识别需要性能改进的区域。

良好的性能要求类似于“每小时有5000个订单时,95%的用户旅程不应超过10秒的等待时间,且没有屏幕转换需要超过1秒”。

这还依赖于在CI管道中部署类似于生产环境的测试环境。

然而,使用性能需求来驱动开发的方式可能仍不是一个好主意,就像你可以使用功能需求一样。对于功能需求,通常在运行之前您有一些洞察力,以便了解应用程序是否会通过测试,并且尝试编写您认为将会通过测试的代码是明智的。而对于性能方面,尝试优化未经测量的代码性能是可疑的做法。您可以使用性能结果在某种程度上驱动应用程序开发,但不能使用性能需求。


0

对于调优本身,您可以直接比较旧代码和新代码。但不要保留两份副本。这听起来像是一场管理噩梦。此外,您只会将一个版本与另一个版本进行比较。功能上的更改可能会减慢您的函数,而这对用户来说是可以接受的。

就我个人而言,我从未见过类型为“必须比上一个版本更快”的性能标准,因为很难衡量。

您说“需要严重的性能调优”。在哪里?哪些查询?哪些函数?是业务方还是用户提出的?什么样的性能是可以接受的?3秒?2秒?50毫秒?

任何性能分析的起点都是定义通过/失败标准。一旦您拥有了这个标准,您就可以自动化性能测试。

对于可靠性,您可以使用(简单的)统计方法。例如,在相同条件下运行相同的查询100次。如果其中95%在n秒内返回,则为通过。

个人而言,我会在集成时从标准机器或集成服务器本身执行此操作。在某个地方记录每个测试的值(Cruise Control有一些很好的功能可以做到这一点)。如果您这样做,就可以看到性能随时间和每个构建而改进的情况。您甚至可以制作一个图表。经理们喜欢图表。

进行性能测试时,无论您是进行自动化测试还是手动测试,始终很难保持稳定的环境。无论您采用什么开发方法(TDD、瀑布流等),都会遇到这个特定的问题。


1
我从未见过“必须比上一个版本更快”的性能标准。WebKit团队对性能退化有零容忍政策。 - KaptajnKold

0

我还没有遇到过这种情况 ;) 但是如果我遇到了,我会这样做(我想我从Dave Astel的书中学到了这个)

步骤1:为“可接受性能”制定规范,例如,这可能意味着“用户需要在N秒(或毫秒)内完成Y”
步骤2:现在编写一个失败的测试。使用友好的计时器类(例如.NET有StopWatch类)和Assert.Less(actualTime, MySpec)
步骤3:如果测试已经通过,则完成。如果是红色的,则需要进行优化并使其变为绿色。一旦测试变为绿色,性能现在是“可接受的”。


0
肯特·贝克和他的团队在TDD中自动化了所有测试。
在性能测试方面,我们也可以在TDD中自动化测试。
在性能测试中的标准是我们应该测试是或否的情况。
如果我们很好地了解规范,我们也可以在TDD中对它们进行自动化。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接