2014-09-03 75 views
1

方法我一直在寻找实现一些代码,我原来写在R导入下的性能改进。到目前为止,事情一直在顺风顺水,但我正在努力的一件事是如何在C中实现S3 Generic/Methods,同时最小化开销。实施S3通用/用C

的应用案例我的函数将大部分时间直接去,我想我可以在通用的实施,或最坏的情况下必须用C默认S3派遣一个级别的默认方法。也就是说,我想允许发生非平凡调度的可能性。

我已经发现了一些引用DispatchOrEval,但似乎这个功能被保留供内部使用,由于在未来的函数定义的src/eval.cattribute_hidden声明:

attribute_hidden 
int DispatchOrEval(SEXP call, SEXP op, const char *generic, SEXP args, 
      SEXP rho, SEXP *ans, int dropmissing, int argsevald) 
{ 

是否有任何其他的方式来以有限的开销直接从C实现S3派遣?

仅供参考,我一直在阅读以下内容:

我承认很多它仍然是相当新的给我,所以我的理解,到目前为止可能是完全错误的。


编辑:解决一个具体的例子马丁的要求,我想写像这样使用时的功能checkArgs(或类似):

FUN <- function(x, y, z) { 
    checkArgs(x=...,y=..., z=...) 
    # More code here 
} 

将提供一个简单的方法快速检查参数是我想要的。

例如,像:

FUN <- function(x, y, z) { 
    checkArgs(x=matrix(numeric(), ncol=3),y=..., z=...) 
    # More code here 
} 

将检查这样的说法x是一个三列数字矩阵。我想允许发送的功能是将xcheckArgs中的模板规范进行比较的功能(此处未显示,但可能会将其称为alike以比较对象的相似性)。大多数情况下,它会使用默认的C版本,但是如果用户为该泛型创建方法并使用该方法的类的对象进行验证,则将使用提供的方法。

为了这通常有用的,包括在SPLIT申请,联合收割机使用的分析,必须checkArgs很快运行。因此,我开始重新编写C中的比较函数,但这样做后,失去了我希望保留的调度功能,即使最常见的用例是依赖于默认方法。

+2

你为什么要试图做到这一点?你确定你的瓶颈是S3派遣吗? – 2014-09-03 02:15:44

+2

我不知道有什么办法可以在不调用R函数的情况下执行S3调度。正如Josh所说,你确定S3会放慢你的速度吗? S3派遣的成本大约为1-2微秒。最后,最近有一些R源的提交加快了S3的发布速度,它们将会在下一个版本中出现。 – wch 2014-09-03 02:30:49

+0

@JoshuaUlrich,是的,我确定。我的函数目前在C中的估计时间为1.5微秒(包括从R开始的初始调用),并且每个S3调度会添加2微秒,其中可能会有几个微秒。功能旨在被作为优化尽可能使得它可以令人信服地添加到被运行数百万次作为部分R的功能拆分应用 - 结合而不过度损害总执行时间分析类型。 – BrodieG 2014-09-03 11:58:54

回答

1

这绝对不是完全令人满意,但解决我的问题的一部分。基本上,我们注册一个虚假的通用那份急件其实之前检查对象性:

genericfun <- function(x) { 
    if(is.object(x)) { 
    UseMethod("genericfun") 
    } else { 
    #.Call(CFunNativeSymbol, x) 
    } 
} 
genericfun.default <- function(x) { 
    #.Call(CFunNativeSymbol, x) 
} 
obj <- structure(1:100, class="obj") 
non.obj <- 1:100 
microbenchmark(
    genericfun(non.obj), 
    genericfun(obj), 
    genericfun.default(non.obj) 
) 

主要生产(注意,这显然是我的Windows系统上使劲的microbenchmark精度):

Unit: nanoseconds 
         expr min lq median uq max neval 
     genericfun(non.obj) 367 732 733.0 1098 16457 100 
      genericfun(obj) 6217 6583 6583.0 6949 35838 100 
genericfun.default(non.obj) 0 1 183.5 366 1463 100 

通用调度现在降到微秒以下。

此外,就递归使用而言,只有在C中的is.object的求值返回TRUE时才需要调用R通用,因此只包含非对象的递归结构无需在任何点从C退出。

有可能使用这种方法(并非最不重要的是它的可怕,黑客性质),这将成为使用明显,但这是我能想出现在最好的许多问题。现在想到的一个问题是,隐式类的调度不会发生(例如is.object(matrix()) == FALSE)。