Rcpp: 结合R与C++提高程序执行效率

Rcpp是一个可以让R透过C++语言提升执行效率的模块,通过它可以将C++引入R中使用,适用于提高性能和接入已有算法库的场合。

Rcpp在R中提供了一个非常简单又好用的C++语言介质,让使用者可以很轻松的结合R与C++,降低程式开发的技术门槛,大幅提高R程式的执行速度。

Rcpp的终极目标是通过C++面向对象与模板编程的机制,把几乎所有R API与数据对象都封装成类以及类的方法。这样,写C++扩展的人只需要了解这些类以及这些类的调用即可,而把接口定义、垃圾回收(gc)、异常捕获、数据类型的转换等等对于R/C++交互都是必须的工作都隐藏起来。

适合使用C++的地方

C++语言虽然执行效率比R语言要高,不过并不是每一种问题都适合使用 C++ 来处理,以下是几种非常适合使用C++来处理的典型问题类型:

  • 无法转换成向量化的循环,例如每次迭代都会跟前一次迭代的运算结果有关
  • 递归函数或是需要大量重复执行的函数,因为在C++中的函数调用会比R中的函数调用更有效率
  • 需要使用R所没有的复杂数据结构

安装Rcpp

由于Rcpp在使用时会需要编译C++代码,所以除了安装R的Rcpp之外,还要在系统上安装 C++编译器。

如果你使用的Linux,安装gcc/g++;如果你使用Windows,需要安装Rtools,这里面包含各种g++。

Rcpp: 结合R与C++加速程序执行

安装Rcpp:

安装完成后,即可导入使用:

使用Rcpp

Rcpp 的 cppFunction 是一个可以让您将 C++ 代码直接内崁至 R 代码中的包装函数,其用法非常简单,在详细介绍之前,我们先来看一个典型的范例,先让大家对 Rcpp 的使用有一个初步的概念。

首先将 R 代码中需要大量运算的部分,改用 C++ 语言编写:

编写好 C++ 的函数之后,直接将 C++ 的整个代码当成参数,传递给 R 的 cppFunction 函数:

cppFunction 会对这段 C++ 代码进行编译,并且载入至目前的 R 环境中,若 C++ 代码没有错误的话,这时候在 R 中就会建立好一个和 C++ 中所定义的函数,调用:

以上就是一个简单的 Rcpp 使用范例,从这个范例你可以看得出来 Rcpp 的使用方式相当简洁,不需要任何手动编译的过程。

下面我们将详细介绍各种参数类型的用法。

一个没有参数、只有单一返回值的 R 函数:

用 C++ 改写:

返回字符串:

R语言:

改为C++:

R for循环:

C++:

由于这里传入的参数 x 会是一个 R 的向量,Rcpp 针对 R 的几种向量特别定义了一些 C++ 的类型供使用者使用,这里我们将 x 的类型定义为浮点数向量 NumericVector,并且使用 NumericVector 的 size 方法函数取得向量的长度,接着定义一个用来储存总和值的 total 变量(由于我们要计算浮点数向量的总和,因此将 total 变量定义为浮点数 double),然后进行后续的 for 循环,最后将计算完成的总和值传回。

以下是各种 R 向量所对应的 C++ 类别:

  • NumericVector 浮点数向量
  • IntegerVector 整数向量
  • CharacterVector 字元向量
  • LogicalVector 逻辑向量

比较R和C++代码的效率:

这里我们测试了一般的 R 函数(r_sum)、C++ 函数(cpp_sum)以及 R 内建的向量化函数(sum),内建的函数执行效率是最好的,而我们自己定义的 C++ 函数稍微逊色一些,而普通 R 函数的执行速度则是比我们定义的 C++ 函数慢了百倍以上。

Rcpp: 结合R与C++加速程序执行

画出测试结果的小提琴图(violin plot):

Rcpp: 结合R与C++加速程序执行

向量输入、向量输出

输入与输出都是向量的函数也很常见,这里我们以一个冒泡排序算法的排序函数做示范,以下是一个 R 版本的排序函数:

C++代码:

矩阵输入、向量输出

Rcpp 同时也对 R 的几种矩阵定义了一些 C++ 的类别:

  • NumericMatrix 浮点数矩阵
  • IntegerMatrix 整数矩阵
  • CharacterMatrix 字元矩阵
  • LogicalMatrix 逻辑矩阵

其使用方式跟向量类似,以下是将 R 的 rowSums 改写成 C++ 的代码:

NumericMatrix 的 nrow 与 ncol 方法可以用来取得该矩阵的列(row)数与行(column)数,而在提取矩阵内部的元素时,是使用小括号 () 运算子。

sourceCpp函數

前面我们介绍过以 cppFunction 函数将 C++ 程式码内崁在 R 程式码中,这样的方式对于简短的小程式而言非常方便,但是如果需要开发比较复杂的 C++ 程式时,把大量的 C++ 程式码贴在 cppFunction 函数中,会让程序难以阅读,在开发上也会很不方便。

遇到比较复杂的程序架构时,我们可以将 C++ 代码独立出来,另外储存成一个 .cpp 文件,然后使用 sourceCpp 函数来编译并载入,这样的作用跟 cppFunction 函数相同。

在C++文件中首先引入Rcpp.h头文件:

在要暴露的C++函数前加:

例如 cpp_sum.cpp:

在R中调用:

我们也可以将 R 的代码内崁在 C++ 的注解之中,其语法为:

放在启动的 R 程式码会以 source(echo = TRUE) 的方式来执行,所以我们不需要特别以 print 等函数来输出,这个功能对于开发阶段的测试与除错会有一些帮助。以下是计算总和的例子:

要获得更多信息,访问:http://www.rcpp.org/

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注