performance - 将使用 S3 的包转换为 S4 类,性能会下降吗?

标签 performance r oop s4

我有一个 R 包,它目前使用 S3 类系统,具有两个不同的类和几种用于通用 S3 函数的方法,如 plotlogLikupdate (用于模型公式更新)。由于没有基于 if/else 中的两个参数的继承或分派(dispatch),我的代码在所有有效性检查和 S3 结构中变得更加复杂,我开始考虑将我的包转换为 S4 。但后来我开始阅读 S3S4 的优缺点,我不再那么确定了。我发现 R-bloggers blog post 关于 S3 与 S4 中的效率问题,就像 5 年前一样,我现在测试了同样的事情:

library(microbenchmark)
setClass("MyClass", representation(x="numeric"))
microbenchmark(structure(list(x=rep(1, 10^7)), class="MyS3Class"),
               new("MyClass", x=rep(1, 10^7)) )
Unit: milliseconds
                                                   expr
 structure(list(x = rep(1, 10^7)), class = "MyS3Class")
                       new("MyClass", x = rep(1, 10^7))
       min       lq   median       uq      max neval
 148.75049 152.3811 155.2263 159.8090 323.5678   100
  75.15198 123.4804 129.6588 131.5031 241.8913   100

所以在这个简单的例子中,S4 实际上要快一点。然后我阅读了 SO question 关于使用 S3S4 的内容,这非常有利于 S3 。特别是@joshua-ulrich 的回答让我怀疑 S4 ,因为它说

any slot change requires a full object copy



如果我考虑到我在优化模型的对数似然时在每次迭代中更新我的对象的情况,那感觉就像一个大问题。经过一番谷歌搜索,我发现了关于这个问题的 John Chambers post,这似乎在 R 3.0.0 中发生了变化。

因此,尽管我觉得使用 S4 类来使我的代码更加清晰(例如从主模型类继承的更多类)以及有效性检查等是有益的,但我现在想知道是否值得所有工作性能?那么,在性能方面, S3S4 之间是否存在真正的性能差异?我还应该考虑其他一些性能问题吗?或者甚至可以对这个问题说些什么?

编辑:正如@DWin 和@g-grothendieck 所建议的,上述基准测试没有考虑现有对象的插槽被更改的情况。所以这是另一个与真实应用程序更相关的基准(示例中的函数可以是模型中某些元素的 get/set 函数,在最大化对数似然时会改变):
objS3<-structure(list(x=rep(1, 10^3), z=matrix(0,10,10), y=matrix(0,10,10)),
                 class="MyS3Class")
fnS3<-function(obj,a){
  obj$y<-a
  obj
}

setClass("MyClass", representation(x="numeric",z="matrix",y="matrix"))
objS4<-new("MyClass", x=rep(1, 10^3),z=matrix(0,10,10),y=matrix(0,10,10))
fnS4<-function(obj,a){ 
  obj@y<-a
  obj
}

a<-matrix(1:100,10,10)
microbenchmark(fnS3(objS3,a),fnS4(objS4,a))
Unit: microseconds
           expr    min     lq median     uq    max neval
 fnS3(objS3, a)  6.531  7.464  7.932  9.331 26.591   100
 fnS4(objS4, a) 21.459 22.393 23.325 23.792 73.708   100

基准测试是在 64 位 Windows 7 上的 R 2.15.2 上执行的。所以这里的 S4 显然更慢。

最佳答案

  • 首先,您可以轻松地为 S4 类提供 S3 方法:
    > extract <- function (x, ...) x@x
    > setGeneric ("extr4", def=function (x, ...){})
    [1] "extr4"
    > setMethod ("extr4", signature= "MyClass", definition=extract)
    [1] "extr4"
    > `[.MyClass` <- extract
    > `[.MyS3Class` <- function (x, ...) x$x
    > microbenchmark (objS3[], objS4 [], extr4 (objS4), extract (objS4))
    Unit: nanoseconds
               expr   min      lq  median      uq   max neval
            objS3[]  6775  7264.5  7578.5  8312.0 39531   100
            objS4[]  5797  6705.5  7124.0  7404.0 13550   100
       extr4(objS4) 20534 21512.0 22106.0 22664.5 54268   100
     extract(objS4)   908  1188.0  1328.0  1467.0 11804   100
    


  • 编辑:由于 Hadley 的评论,将实验更改为 plot :
    > `plot.MyClass` <- extract
    > `plot.MyS3Class` <- function (x, ...) x$x
    > microbenchmark (plot (objS3), plot (objS4), extr4 (objS4), extract (objS4))
    Unit: nanoseconds
               expr   min      lq median      uq     max neval
        plot(objS3) 28915 30172.0  30591 30975.5 1887824   100
        plot(objS4) 25353 26121.0  26471 26960.0  411508   100
       extr4(objS4) 20395 21372.5  22001 22385.5   31359   100
     extract(objS4)   979  1328.0   1398  1677.0    3982   100
    
    plot 的 S4 方法我得到:
        plot(objS4) 19835 20428.5 21336.5 22175.0 58876   100
    

    所以是的,[有一个异常快速的调度机制(这很好,因为我认为提取和相应的替换函数是最常调用的方法之一。但是不,S4 调度并不比 S3 调度慢。

    这里 S4 对象上的 S3 方法与 S3 对象上的 S3 方法一样快。但是,没有调度的调用仍然更快。
  • 有些东西比 S3 效果更好,例如 as.matrixas.data.frame出于某种原因,将这些定义为 S3 意味着例如lm (formula, objS4)开箱即用。这不适用于 as.data.frame被定义为 S4 方法。
  • 调用debug更方便。在 S3 方法上。
  • 其他一些东西不适用于 S3,例如调度第二个参数。
  • 性能是否会明显下降显然取决于你的类,即你有什么样的结构、对象有多大以及方法被调用的频率。几微秒的方法调度与毫秒甚至秒的计算无关。但是当一个函数被调用数十亿次时,μs 确实很重要。
  • 导致某些经常调用的函数( [ )性能显着下降的一件事是 S4 验证(在 validObject 中完成了相当多的检查) - 但是,我很高兴拥有它,所以我使用它。在内部,我使用跳过此步骤的主力函数。
  • 如果您有大量数据并且引用调用会帮助您提高性能,您可能需要查看引用类。到目前为止,我从未真正与他们合作过,所以我无法对此发表评论。
  • 关于performance - 将使用 S3 的包转换为 S4 类,性能会下降吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15590656/

    相关文章:

    mysql - 在 Mysql 中缓存月末和月初。 Node.js 中的 Mysql 变量重新定义

    python - 文本处理 - Python 与 Perl 的性能对比

    c++ - 锁定取消引用的互斥量是不好的行为吗?

    php - 有人知道一个好的 OOP PHP MySQL 类吗?主要构建查询

    java - 在 JTextArea 中显示样式内容

    WCF readerQuotas 设置 - 缺点?

    从每个字符串的末尾删除特定字符

    r - 将结果专门保存在另一个 data.table 中

    r - dplyr/rlang : parse_expr with multiple expressions

    .net - 我应该继承还是订阅事件?