【发布时间】:2023-03-20 01:46:01
【问题描述】:
我一直在寻求将我最初用 R 编写的一些代码实现到 C 中,以提高性能。到目前为止,事情进展顺利,但我正在努力解决的一项问题是如何在 C 中实现 S3 通用/方法,同时最大限度地减少开销。
我的函数的用例大部分时间会直接转到我认为我可以在泛型中实现的默认方法,或者最坏的情况是在 C 中将一级 S3 分派到默认值。也就是说,我希望允许发生非平凡调度的可能性。
我发现了一些对 DispatchOrEval 的引用,但由于在函数定义之前 src/eval.c 中的 attribute_hidden 声明,该函数似乎保留供内部使用:
attribute_hidden
int DispatchOrEval(SEXP call, SEXP op, const char *generic, SEXP args,
SEXP rho, SEXP *ans, int dropmissing, int argsevald)
{
有没有其他方法可以在有限的开销下直接从 C 实现 S3 调度?
作为参考,我一直在阅读以下内容:
- 一个blog post讨论
attribute_hidden - 哈德利的Advanced R book referencing
DispatchOrEval - 编写 R 扩展
-
src/eval.c和其他人
我承认其中很多内容对我来说还是很新的,所以到目前为止我的理解可能完全错误。
编辑:为了解决马丁对特定示例的要求,我正在尝试编写一个函数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 是一个三列数字矩阵。我要允许调度的函数是将x 与checkArgs 中的模板规范进行比较的函数(此处未显示,但可能会称为alike 以比较对象的相似性) .大多数情况下,它会使用默认的 C 版本,但如果用户为该泛型创建一个方法并使用该方法类的对象进行验证,那么将使用提供的方法。
为了使其普遍有用,包括在 SPLIT-APPLY-COMBINE 分析中的使用,checkArgs 必须非常快速地运行。因此我开始重写C 中的比较函数,但这样做失去了我想保留的调度能力,即使最常见的用例是依赖默认方法。
【问题讨论】:
-
你为什么要这样做?您确定您的瓶颈是 S3 调度吗?
-
我不知道有什么方法可以在不调用 R 函数的情况下进行 S3 调度。正如 Josh 所说,您确定 S3 会减慢您的速度吗? S3 调度的成本大约为 1-2 微秒。最后,最近对 R 源代码的一些提交可以加快 S3 调度,它们应该会出现在下一个版本中。
-
@JoshuaUlrich,是的,我很确定。我的函数目前在 C 语言中的评估时间为 1.5 微秒(包括来自 R 的初始调用),并且每次 S3 调度会增加 2 微秒,其中可能有几个。该函数旨在尽可能优化,以便可以将其添加到作为拆分-应用-组合类型分析的一部分运行数百万次的 R 函数中,而不会过度影响整体执行时间。
-
@wch,感谢您对即将到来的 S3 改进的提醒。 “我不知道如何在不调用 R 函数的情况下进行 S3 调度”,您的意思是“无法从 C 中访问内部 R 方法”?
-
很高兴看到一个明确的(简化的)示例;由于 S3 调度已经在 C 中,目前尚不清楚通过自己从 C 调用它(通过 R 函数调用的 C 级公式,正如我认为@wch 所考虑的那样)可以获得什么。但也许还有其他解决方案,就像记忆和我们的老朋友矢量化可能在分裂之前,这会有所帮助。