C++が使えてRcppパッケージがワークすることの確認

  • 資料
  • C++が使えてRcppパッケージがワークすることの確認
library(Rcpp)
cppFunction('int add(int x, int y, int z) {
  int sum = x + y + z;
  return sum;
}')
add
add(3,4,5)
    • c++コードになっている'int...'がコンパイルされて、pointer: 0x0000000005d81770と表された「コンピュータ上の場所」に格納されたコンパイル済みのライブラリとして作成されているので、それを.Callによって呼び出していることがわかる
> add
function (x, y, z) 
.Primitive(".Call")(<pointer: 0x0000000005d81770>, x, y, z)
> add(3,4,5)
[1] 12
  • Cppのソースを書いて、それをRcppのsourceCpp()関数で読み込み、作ったR用の関数を使ってみる、という一連の作業を一発でやるために、Cppソースの中にR実行部分を書き込んでおくことができる。
    • 次のようなCppコードをmytest.cppという名前で保存し、そのうえで、示したようなRコマンドを実行した結果を示す
#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
double meanC(NumericVector x) {
  int n = x.size();
  double total = 0;

  for(int i = 0; i < n; ++i) {
    total += x[i];
  }
  return total / n;
}

/*** R
library(microbenchmark)
x <- runif(1e5)
microbenchmark(
  mean(x),
  meanC(x)
)
*/
rceCpp("mytest.cpp",verbose=TRUE)

Generated extern "C" functions 
--------------------------------------------------------


#include <Rcpp.h>
// meanC
double meanC(NumericVector x);
RcppExport SEXP sourceCpp_44351_meanC(SEXP xSEXP) {
BEGIN_RCPP
    Rcpp::RObject __result;
    Rcpp::RNGScope __rngScope;
    Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP);
    __result = Rcpp::wrap(meanC(x));
    return __result;
END_RCPP
}

Generated R functions 
-------------------------------------------------------

`.sourceCpp_44351_DLLInfo` <- dyn.load('C:/Users/ryamada/AppData/Local/Temp/RtmpCiGfLb/sourcecpp_1c946232410a/sourceCpp_62222.dll')

meanC <- Rcpp:::sourceCppFunction(function(x) {}, FALSE, `.sourceCpp_44351_DLLInfo`, 'sourceCpp_44351_meanC')

rm(`.sourceCpp_44351_DLLInfo`)

Building shared library
--------------------------------------------------------

DIR: C:/Users/ryamada/AppData/Local/Temp/RtmpCiGfLb/sourcecpp_1c946232410a

C:/PROGRA~1/R/R-31~1.0/bin/x64/R CMD SHLIB -o "sourceCpp_62222.dll" "" "mytest.cpp"  
g++ -m64 -I"C:/PROGRA~1/R/R-31~1.0/include" -DNDEBUG     -I"C:/Users/ryamada/Documents/R/win-library/3.1/Rcpp/include" -I"c:/Users/ryamada/Desktop/Rcpp"  -I"d:/RCompile/CRANpkg/extralibs64/local/include"     -O2 -Wall  -mtune=core2 -c mytest.cpp -o mytest.o
g++ -m64 -shared -s -static-libgcc -o sourceCpp_62222.dll tmp.def mytest.o -Ld:/RCompile/CRANpkg/extralibs64/local/lib/x64 -Ld:/RCompile/CRANpkg/extralibs64/local/lib -LC:/PROGRA~1/R/R-31~1.0/bin/x64 -lR

> library(microbenchmark)

> x <- runif(1e5)

> microbenchmark(
+   mean(x),
+   meanC(x)
+ )
Unit: microseconds
     expr     min      lq      mean  median      uq     max neval
  mean(x) 188.544 188.924 193.53921 188.924 189.305 315.888   100
 meanC(x)  91.992  92.372  95.61786  92.752  93.131 126.963   100
 警告メッセージ: 
package ‘microbenchmark’ was built under R version 3.1.2
  • 少し説明を加える
    • 以下はsourceCpp()関数が作ったCppの関数そのもの。RとCppとではデータ型の扱いなどが違うのでその橋渡しをするために色々しなくてはならないことがあるのだが、その面倒を担って、以下のようなものを作ってくれた上でコンパイルに進めるのがsourceCpp()の仕事、ともいえる
double meanC(NumericVector x);
RcppExport SEXP sourceCpp_44351_meanC(SEXP xSEXP) {
BEGIN_RCPP
    Rcpp::RObject __result;
    Rcpp::RNGScope __rngScope;
    Rcpp::traits::input_parameter< NumericVector >::type x(xSEXP);
    __result = Rcpp::wrap(meanC(x));
    return __result;
END_RCPP
}
    • 以下はコマンドライン用Rでパッケージを作るときなどにも使う関数を使って、ローカルPC上にライブラリを作ったりしていることを示している
      • 適当に決めた名前で作るよ!と言う感じ
      • g++を使ってコンパイルしているよ、g++の実行オプションやらパスやらも「知っている」からやってあげる、と書いてある
`.sourceCpp_44351_DLLInfo` <- dyn.load('C:/Users/ryamada/AppData/Local/Temp/RtmpCiGfLb/sourcecpp_1c946232410a/sourceCpp_62222.dll')

meanC <- Rcpp:::sourceCppFunction(function(x) {}, FALSE, `.sourceCpp_44351_DLLInfo`, 'sourceCpp_44351_meanC')

rm(`.sourceCpp_44351_DLLInfo`)

DIR: C:/Users/ryamada/AppData/Local/Temp/RtmpCiGfLb/sourcecpp_1c946232410a

C:/PROGRA~1/R/R-31~1.0/bin/x64/R CMD SHLIB -o "sourceCpp_62222.dll" "" "mytest.cpp"  
g++ -m64 -I"C:/PROGRA~1/R/R-31~1.0/include" -DNDEBUG     -I"C:/Users/ryamada/Documents/R/win-library/3.1/Rcpp/include" -I"c:/Users/ryamada/Desktop/Rcpp"  -I"d:/RCompile/CRANpkg/extralibs64/local/include"     -O2 -Wall  -mtune=core2 -c mytest.cpp -o mytest.o
g++ -m64 -shared -s -static-libgcc -o sourceCpp_62222.dll tmp.def mytest.o -Ld:/RCompile/CRANpkg/extralibs64/local/lib/x64 -Ld:/RCompile/CRANpkg/extralibs64/local/lib -LC:/PROGRA~1/R/R-31~1.0/bin/x64 -lR
    • このようにしてローカルPCにコンパイル済みのDLL(ダイナミックリンクライブラリ)ができているので、それを呼び出すようなR関数を作ることでRから呼び出せるようになる。もう一度R関数がDLLを呼び出している様子を書けば:
> add
function (x, y, z) 
.Primitive(".Call")(<pointer: 0x0000000005d81770>, x, y, z)