This document provides some comparisons of three methods to call C from R: .C
, .Call
, and Rcpp. Here are descriptions for these methods:
In this example I want to compute the row sums of a double matrix, mimicing the rowSums
function in R.
Here is the C function myRowSum.c for using .C
and .Call
. There are two functions: myRowSum_C
is to use .C
, and myRowSum_Call
uses .Call
.
#include <stdio.h>
#include <R.h>
#include <Rinternals.h>
void myRowSum_C(double *x, int *nrowx, int *ncolx, double *result) {
int i, j;
for(i=0; i<nrowx[0]; i++)
for(j=0; j<ncolx[0]; j++)
result[i] = result[i] + x[j*nrowx[0]+i];
}
SEXP myRowSum_Call(SEXP X) {
int i, j, nrowx, ncolx;
double *x_ptr;
/* extract the "dim" attribute */
SEXP dims;
dims = getAttrib( X, R_DimSymbol ) ;
nrowx = INTEGER(dims)[0];
ncolx = INTEGER(dims)[1];
x_ptr = REAL(X);
/* allocate memory for the result vector */
SEXP result = PROTECT( allocVector(REALSXP, nrowx) );
double *result_ptr = REAL(result);
for(i=0; i<nrowx; i++) {
result_ptr[i] = 0.0;
for(j=0; j<ncolx; j++)
result_ptr[i] = result_ptr[i] + x_ptr[j*nrowx+i];
}
UNPROTECT(1);
return(result);
}
I compiled the above file outside R using R CMD SHLIB myRowSum.c
, which generates share library myRowSum.so
.
Here is the cpp function myRowSum_Rcpp.cpp for using Rcpp
. This function will be compiled in R using sourceCpp
R funtion.
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector myRowSum_Rcpp( NumericMatrix x ) {
int nrow = x.nrow(), ncol = x.ncol();
int i, j;
double tmp;
NumericVector result(nrow);
for(i=0; i<nrow; i++) {
tmp = 0.0;
for(j=0; j<ncol; j++)
tmp = tmp + x(i,j);
result[i] = tmp;
}
return(result);
}
Below are the R codes for comparison. 1. First load in necessary packages, shared libray, and compile the Rcpp code.
library(Rcpp)
library(microbenchmark)
dyn.load("myRowSum.so")
sourceCpp("myRowSum_Rcpp.cpp")
myRowSum_C
and myRowSum_Call
functions from C.myRowSum_C <- function(X) {
.C("myRowSum_C", X, as.integer(nrow(X)), as.integer(ncol(X)), rep(0,nrow(X)))[[4]]
}
myRowSum_Call <- function(X) {
.Call("myRowSum_Call", X)
}
rowSums
R function is also being compared.X = matrix(rnorm(1e7,sd=5), ncol=10)
microbenchmark(myRowSum_C(X), myRowSum_Call(X), myRowSum_Rcpp(X), rowSums(X), times=100)
## Unit: milliseconds
## expr min lq mean median uq
## myRowSum_C(X) 34.848010 40.890005 50.549986 48.308802 54.529563
## myRowSum_Call(X) 8.314495 9.923796 12.303820 10.662178 11.869721
## myRowSum_Rcpp(X) 6.197581 7.396726 9.964185 8.096877 9.242808
## rowSums(X) 21.238879 24.793712 27.591730 26.218080 28.845653
## max neval cld
## 98.69287 100 c
## 40.09937 100 a
## 66.39647 100 a
## 52.33727 100 b
It can be seen that on average, myRowSum_Call
takes about 12 milliseconds, myRowSum_Rcpp
takes around 10 milliseconds, and myRowSum_C
is in the slowest taking 50 milliseconds. As a benchmark, the rowSums
function in R takes ~29 milliseconds. Of course that function has some other error checking machenism, which slows it down. These numbers will vary from run to run, but it appears Rcpp
is the fastest, followed by .Call
. .C
is the slowest by a large margin. The discrepency is so big that it is suprising.
The above example only tests the opertaions on loop and sum. Now I want to throw in multiplication. In this example, I implement the linear kernel smoothing procedure, mimicing the filter
function in R. This time I will just compare .Call
and Rcpp
.
Here is the C funnction using .Call
myFilter_Call.c:
#include <R.h>
#include <Rinternals.h>
SEXP myFilter_Call( SEXP x, SEXP f ) {
int nx = length(x), nf = length(f);
int i, j;
double tmp;
SEXP result = PROTECT( allocVector(REALSXP, nx) );
double *result_ptr = REAL(result);
double *x_ptr = REAL(x), *f_ptr = REAL(f);
int half_nf = (nf+1)/2;
for(i=half_nf-1; i<nx - half_nf; i++) {
tmp = 0.0;
for(j=0; j<nf; j++) {
tmp += x_ptr[i-half_nf+1+j] * f_ptr[j];
}
result_ptr[i] = tmp;
}
UNPROTECT(1);
return(result);
}
Here is the cpp funnction using Rcpp
myFilter_Rcpp.cpp:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector myFilter_Rcpp( NumericVector x, NumericVector f ) {
int nx = x.length(), nf = f.length();
int i, j;
double tmp;
NumericVector result(nx);
int half_nf = (nf+1)/2;
for(i=half_nf-1; i<nx - half_nf; i++) {
tmp = 0.0;
for(j=0; j<nf; j++) {
tmp += x[i-half_nf+1+j]*f[j];
}
result[i] = tmp;
}
return(result);
}
I then use the following R code to do the comparison. Here I use a Gaussian kernel with length 9 to smooth 1 million data points.
library(Rcpp)
library(microbenchmark)
sourceCpp("myFilter_Rcpp.cpp")
dyn.load("myFilter_Call.so")
myFilter_Call <- function(X, f) {
.Call("myFilter_Call", X, f)
}
X = rnorm(1e6)
f = dnorm(qnorm(seq(0.1, 0.9, by=0.1)))
f = f/sum(f) ## Gaussian kernel
microbenchmark( myFilter_Call(X,f), myFilter_Rcpp(X,f), times=100)
## Unit: milliseconds
## expr min lq mean median uq
## myFilter_Call(X, f) 5.382100 6.465250 9.372279 8.658638 9.99313
## myFilter_Rcpp(X, f) 6.311626 7.809008 10.110947 9.800286 10.41113
## max neval cld
## 38.76324 100 a
## 34.25049 100 a
Again, Rcpp
and .Call
have comparable performances.
Based on the comparisons above, .Call
as an old-fashion way to interface with C code provides similar performance as Rcpp
. However, Rcpp
has the easiest interface, whereas .Call
requires a little bit more programming in manipulating the input/output objects. Thus, Rcpp
seems to be the best interface to C from R.