代码之家  ›  专栏  ›  技术社区  ›  Collin

在哪里可以学习如何编写C代码来加速慢R函数?[关闭]

  •  108
  • Collin  · 技术社区  · 14 年前

    学习如何编写与R一起使用的C代码的最佳资源是什么?我知道 system and foreign language interfaces R扩展的一节,但我觉得很难。有什么好的资源(在线和离线)可以编写C代码用于R?

    为了澄清,我不想学习如何编写C代码,我想学习如何更好地集成R和C。例如,如何从C整数向量转换为R整数向量(或反之亦然),或从C标量转换为R向量?

    4 回复  |  直到 10 年前
        1
  •  68
  •   Dirk is no longer here    14 年前

    好吧,有个好老头 利用消息来源,卢克! ---R本身有很多(非常有效的)C代码可以学习,CRAN有数百个包,其中一些来自您信任的作者。这为学习和适应提供了真实的、经过检验的例子。

    但正如Josh所怀疑的,我更倾向于C++。 Rcpp . 它也有很多例子。

    我发现有两本书很有帮助:

    • S程序设计
    • 数据分析软件 “这是最新的,并且有一个更好的R-中心的感觉-和两个章节延伸R。另外,约翰因为我的所作所为把我撕成碎片 digest 所以光是这一点就值得你付出代价。

    Rcpp公司 Rcpp公司

    编辑2: 关于哈德利的问题,我 非常强烈 很容易避免 . 看看 Rcpp-introduction vignette this blog post 在这里,我不必担心10%的差异(在Radford-Neal的一个例子中),我们可以得到 八倍 用C++来增加(当然是一个虚构的例子)。

    很复杂的是,你可能会遇到C++错误,简单地说,很难理解GROK。但是为了 使用Rcpp 成本 是不可否认的,它被 利益

        2
  •  54
  •   Romain Francois    14 年前

    哈德利,

    我理解你所说的C++比C更复杂。这是如果你想掌握一切:对象、模板、STL、模板元编程等等…大多数人不需要这些东西,只能依靠别人。Rcpp的实现是非常复杂的,但仅仅因为你不知道你的冰箱是如何工作的,并不意味着你不能打开门去抓鲜奶。。。

    从你对R的许多贡献中,让我印象深刻的是,你发现R有些乏味(数据操作、图形、字符串操作等等)。准备好迎接更多的惊喜吧,使用R的内部C API,这是非常乏味的。

    Rcpp旨在消除API的这些繁琐方面。

    你可以自己判断你发现什么更复杂,更模糊,等等。。。基于几个例子。此函数使用C API创建字符向量:

    SEXP foobar(){
      SEXP ab;
      PROTECT(ab = allocVector(STRSXP, 2));
      SET_STRING_ELT( ab, 0, mkChar("foo") );
      SET_STRING_ELT( ab, 1, mkChar("bar") );
      UNPROTECT(1);
    }
    

    使用Rcpp,可以编写与以下相同的函数:

    SEXP foobar(){
       return Rcpp::CharacterVector::create( "foo", "bar" ) ;
    }
    

    SEXP foobar(){
       Rcpp::CharacterVector res(2) ;
       res[0] = "foo" ;
       res[1] = "bar" ;
       return res ;
    }
    

    正如德克所说,在这几个小插曲中还有其他的例子。我们通常也会将人们引向我们的单元测试,因为每个单元测试都测试代码的一个非常特定的部分,并且有些不言自明。

    我显然有偏见,但我建议大家熟悉Rcpp,而不是学习R的C API,如果Rcpp有什么不清楚或似乎不可行的地方,可以来邮件列表。

    不管怎样,推销结束了。

    我想这一切都取决于你最终想写什么样的代码。

        3
  •  28
  •   Romain Francois    14 年前

    哈德利:不幸的是,我没有专门的资源来帮助你开始C++。我从Scott Meyers的书(有效C++,更有效的C++等等……)中获取了这些,但这些并不是人们所能称之为入门的东西。

    • C++函数必须返回一个R对象。所有R对象都是SEXP。
    • C++函数将0个和65个R对象作为输入(再加上SSIP)。
    • 外部“C” RcppExport公司

    因此.Call函数在某个头文件中声明如下:

    #include <Rcpp.h>
    
    RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;
    

    SEXP foo( SEXP x1, SEXP x2 ){
       ...
    }
    

    关于使用Rcpp的R-API,我们没有更多的了解。

    大多数人只想在Rcpp中处理数值向量。你可以用NumericVector类来实现这一点。有几种方法可以创建数值向量:

    从您从R传递下来的现有对象:

     SEXP foo( SEXP x_) {
        Rcpp::NumericVector x( x_ ) ;
        ...
     }
    

    对于使用::create static函数的给定值:

     Rcpp::NumericVector x = Rcpp::NumericVector::create( 1.0, 2.0, 3.0 ) ;
     Rcpp::NumericVector x = Rcpp::NumericVector::create( 
        _["a"] = 1.0, 
        _["b"] = 2.0, 
        _["c"] = 3
     ) ;
    

     Rcpp::NumericVector x( 10 ) ;      // filled with 0.0
     Rcpp::NumericVector x( 10, 2.0 ) ; // filled with 2.0
    

    一旦你有了一个向量,最有用的是从中提取一个元素。这是由运算符[]完成的,使用基于0的索引,例如,求数值向量的值与此类似:

    SEXP sum( SEXP x_ ){
       Rcpp::NumericVector x(x_) ;
       double res = 0.0 ;
       for( int i=0; i<x.size(), i++){
          res += x[i] ;
       }
       return Rcpp::wrap( res ) ;
    }
    

    但有了Rcpp糖,我们现在可以做得更好:

    using namespace Rcpp ;
    SEXP sum( SEXP x_ ){
       NumericVector x(x_) ;
       double res = sum( x ) ;
       return wrap( res ) ;
    }
    

        4
  •  19
  •   Romain Francois    14 年前

    @jbremaint:没错。Rcpp类实现了一些接近RAII模式的东西。创建Rcpp对象时,构造函数会采取适当的措施确保底层R对象(SEXP)不受垃圾收集器的影响。毁灭者撤回了保护。这在 Rcpp-intrduction 小插曲。底层实现依赖于R API函数 保留对象 释放对象

    由于C++封装,确实存在性能损失。我们尽量在内联等方面保持最低限度。。。代价很小,如果考虑到编写和维护代码所需时间的收益,就没有那么重要了。

    从Rcpp类函数调用R函数比直接使用C api调用eval慢。这是因为我们采取预防措施并将函数调用封装到TryCcatch块中,以便捕获R错误并将它们推广到C++异常,以便使用C++中的标准TIG/catch来处理它们。

    大多数人都想使用向量(特别是数字向量),这个类的惩罚很小。examples/convalvebenchmarks目录包含R-exts中臭名昭著的卷积函数的几个变体,vignette有基准测试结果。事实证明,Rcpp比使用R-API的基准代码更快。