防止R中的性能回归

时间:2011-12-11 15:17:17

标签: performance r testing

检测R包中的性能回归有什么好的工作流程?理想情况下,我正在寻找与R CMD check集成的东西,当我在代码中引入显着的性能回归时,它会提醒我。

一般来说什么是好的工作流程?还有哪些其他语言提供的好工具它是否可以建立在最高单元测试之上,或者通常是单独完成的?

3 个答案:

答案 0 :(得分:17)

这是一个非常具有挑战性的问题,也是我经常处理的问题,因为我在一个软件包中交换了不同的代码以加快速度。有时,性能回归伴随着算法或实现的变化,但也可能由于所使用的数据结构的变化而出现。

  

检测R包中的性能回归有什么好的工作流程?

在我的情况下,我倾向于使用非常具体的用例,我试图加速,使用不同的固定数据集。正如Spacedman所写,拥有固定计算系统非常重要,但这几乎是不可行的:有时共享计算机可能有其他进程可以将速度降低10-20%,即使它看起来很空闲。

我的步骤:

  1. 标准化平台(例如,一台或几台机器,特定虚拟机,或虚拟机+特定基础架构,亚马逊的EC2实例类型)。
  2. 标准化将用于速度测试的数据集。
  3. 创建涉及极少数据转换的脚本和固定的中间数据输出(即保存到.rdat文件)。我的重点是某种建模,而不是数据操作或转换。这意味着我想为建模功能提供完全相同的数据块。但是,如果数据转换是目标,那么请确保预转换/操作数据尽可能接近不同版本包的测试标准。 (有关可用于标准化或加速非焦点计算的记忆,缓存等示例,请参阅this question。它通过OP引用了几个包。)
  4. 多次重复测试。
  5. 相对于固定基准比例缩放结果,例如进行线性回归,对矩阵进行排序等的时间。这可以允许" local"或基础设施的瞬态变化,例如可能由于I / O,存储器系统,依赖包等等。
  6. 尽可能严格地检查分析输出(有关某些见解,请参阅this question,同时参考OP中的工具)。

      

    理想情况下,我正在寻找与R CMD检查集成的内容,以便在我的代码中引入显着的性能回归时提醒我。

    不幸的是,我没有答案。

      

    一般来说什么是好的工作流程?

    对我而言,它与一般的动态代码测试非常相似:输出(在这种情况下的执行时间)是否可重现,最佳和透明?透明度来自于了解影响整体时间的因素。这是迈克·邓拉维的建议很重要的地方,但我更倾向于使用直线轮廓仪。

    关于行分析器,请参阅my previous question,其中引用Python和Matlab中的选项以获取其他示例。检查时钟时间是最重要的,但跟踪内存分配,执行行的次数和调用堆栈深度也非常重要。

      

    其他哪些语言提供了很好的工具?

    几乎所有其他语言都有更好的工具。 :)像Python和Matlab这样的解释语言有很好的&可能熟悉的工具示例可以适用于此目的。尽管动态分析非常重要,但静态分析可以帮助确定可能存在严重问题的位置。例如,Matlab有一个很好的静态分析器,可以报告对象(例如矢量,矩阵)何时在循环内生长。仅通过动态分析找到它是很糟糕的 - 你已经浪费了执行时间来发现这样的事情,并且如果你的执行上下文非常简单(例如只是几次迭代,或者它,它并不总是可辨别的)小物件)。

    就语言无关的方法而言,您可以查看:

    1. Valgrind& cachegrind
    2. 监视磁盘I / O,脏缓冲区等
    3. 监控RAM(Cachegrind很有用,但您可以监控RAM分配,以及有关RAM使用的大量详细信息)
    4. 使用多个核心
    5.   

      是否可以在顶级单元测试中构建,或者通常单独完成?

      这很难回答。对于静态分析,它可以在单元测试之前进行。对于动态分析,可能需要添加更多测试。可以将其视为顺序设计(即来自实验设计框架):如果执行成本看起来是在变量的某些统计容差内,那么就不需要进一步的测试。但是,如果方法B的平均执行成本似乎大于方法A,那么应该进行更密集的测试。


      更新1:如果我可能会如此大胆,那么我推荐的另一个问题包括:"比较两个版本的执行时间有什么问题?打包&#34?;这类似于假设实现相同算法的两个程序应该具有相同的中间对象。这并不完全正确(参见this question - 并非我在推广自己的问题,而不是在这里 - 让事情变得更好,更快......这是一项艰巨的工作......关于这个话题的问题:))。以类似的方式,由于实现以外的因素,相同代码的两次执行可能在时间上有所不同。

      因此,可能会出现一些问题,无论是在同一语言内还是跨语言,在同一个执行实例中,或者相同的"相同的"实例,可能影响运行时:

      1. 垃圾收集 - 不同的实现或语言可以在不同情况下进行垃圾收集。这可能会使两个执行看起来不同,但它可能非常依赖于上下文,参数,数据集等.GC-obsessive执行看起来会更慢。
      2. 在磁盘,主板(例如L1,L2,L3高速缓存)或其他级别(例如,memoization)的级别进行高速缓存。通常,第一次执行将支付罚金。
      3. Dynamic voltage scaling - 这个很糟糕。当出现问题时,这可能是最困难的事情之一,因为它可以迅速消失。它看起来像缓存,但它不是。
      4. 您不了解的任何职位优先经理。
      5. 一种方法使用多个核心,或者做一些关于如何在核心或CPU之间分配工作的聪明的东西。例如,在某些情况下,将进程锁定到核心可能很有用。在这方面,一个R包的执行可能更幸运,另一个包可能非常聪明......
      6. 未使用的变量,过多的数据传输,脏缓存,未刷新的缓冲区......列表继续。
      7. 关键结果是:理想情况下,我们应该如何测试预期值的差异,这取决于因订单效应而产生的随机性?嗯,非常简单:回到实验设计。 :)

        当执行时间的经验差异与预期的"差异,能够进行额外的系统和执行监控非常棒,这样我们就不必重新运行实验,直到我们面对蓝色。

答案 1 :(得分:10)

在这里做任何事情的唯一方法是做出一些假设。因此,让我们假设一台未更改的机器,或者需要'重新校准'。

然后使用单元测试相似的框架,并将“必须在X单位时间内完成”作为另一个要实现的测试标准。换句话说,做一些像

这样的事情
 stopifnot( timingOf( someExpression ) < savedValue plus fudge)

所以我们必须将先前的时间与给定的表达相关联。也可以使用三个现有单元测试包中的任何一个的等式测试比较。

Hadley无法处理任何事情,所以我认为在下一个长时间的学术休息后我们几乎可以期待一个新的包timr :)。当然,这必须是可选的,因为在“未知”的机器上(想想:CRAN测试包)我们没有参考点,否则软糖因素必须“转到11”才能在新机器上自动接受

答案 2 :(得分:4)

最近公布的R-devel饲料变化可能会为此提供一个粗略的衡量标准。

  

R-devel实用程序的变化

     

'R CMD check'可以选择报告检查各个部分的时间:这是由'Writing R Extensions'中记录的环境变量控制的。

请参阅http://developer.r-project.org/blosxom.cgi/R-devel/2011/12/13#n2011-12-13

可以检查运行测试所花费的总时间,并与之前的值进行比较。当然,添加新测试会增加时间,但仍然可以看到显着的性能回归,尽管是手动的。

这不像单个测试套件中的时序支持那么精细,但它也不依赖于任何一个特定的测试套件。