R:使用class属性更慢地列出元素的赋值

时间:2015-03-18 12:53:06

标签: r performance list oop

我在S3和S4类的上下文中进行了一些分析,并观察了以下内容:

与相应普通列表上的相同操作相比,对S3对象元素的简单值赋值大约慢2-3倍。

从我的角度来看,S3类是一个带有附加属性的列表,元素只是一个数字。那么,哪些机制消耗额外的时间?

value <- 1 
obj_list <- list( a = 0 )
obj_s3 <- structure( obj_list, class = "myclass" )

system.time( 
  replicate( 100000, obj_list$a <- value)
) # ~180 ms

system.time( 
  replicate( 100000, obj_s3$a <- value)
) # ~420 ms

1 个答案:

答案 0 :(得分:8)

只要在R变量中添加一个类,就会使其成为受S3调度影响的对象。由于$<-的作用类似于S3泛型,$<-将尝试根据对象的类进行调度。如果查看$<-的C代码,可以看到:

/* From src/main/subassign.c

   $<-(x, elt, val)
*/
SEXP attribute_hidden do_subassign3(SEXP call, SEXP op, SEXP args, SEXP env)
{
  // ... code omitted  
  if(DispatchOrEval(call, op, "$<-", args, env, &ans, 0, 0))
      return(ans);
  // ... code omitted  
}
如果参数是一个对象(即具有一个类或是一个S4对象),

DispatchOrEval将仅启动S3调度。请注意,即使对于没有方法的对象,S3 dispatch也会产生开销,因为仍然必须找到默认方法。如果我们查看像mean这样的非原始S3泛型,那么这一点就更清楚一点了,显然调度过程是:

> mean
function (x, ...) 
UseMethod("mean")
<bytecode: 0x000000000fd151c0>
<environment: namespace:base>

这表明当您在没有方法的情况下在对象上调用mean时,会发生以下情况:

mean(obj) => UseMethod() => find method => mean.default(obj)

额外调用和查找匹配方法的过程会增加您正在观察的开销。对于像$<-sum这样的内容,这一点并不明显,因为所有内容都是通过DispatchOrEval中的C代码完成的。

举例说明:

> obj <- structure(1:10, class="wookkawooka")
> var <- 1:10
> 
> library(microbenchmark)
> microbenchmark(mean(obj), mean(var), mean.default(obj))
Unit: microseconds
              expr    min     lq     mean median      uq    max neval
         mean(obj) 12.069 13.166 16.46442 13.166 13.7145 95.813   100
         mean(var)  8.046  8.777  9.51974  8.778  9.1430 31.084   100
 mean.default(obj)  6.217  7.314  9.17234  7.680  8.0460 84.111   100

请注意,差异在此处未显示,因为mean.default函数本身比$<-等基元具有更多开销,因此调度时间占总时间的比例较小。另外,请注意,对于非对象,调度仍然会发生(与基元不同),除了可以更快地决定使用默认方法。这就是mean(var)mean.default(obj)慢一点但比mean(obj)快的原因。

以下是您可能感兴趣的 a blog post on S3/S4 dispatch performance