Differenza di prestazione tra RCPPARMADILLO e ARMADILLO
Domanda
Sto cercando di capire una differenza nelle prestazioni tra una funzione scritta in RCPParmadillo e uno scritto in un programma standalone C ++ utilizzando la Biblioteca Armadillo. Ad esempio, considerare la seguente funzione semplice che calcola i coefficienti per un modello lineare utilizzando la formula del libro di testo tradizionale.
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
using namespace Rcpp;
using namespace arma;
// [[Rcpp::export]]
void simpleLm(NumericMatrix Xr, NumericMatrix yr) {
int n = Xr.nrow(), k = Xr.ncol();
mat X(Xr.begin(), n, k, false);
colvec y(yr.begin(), yr.nrow(), false);
colvec coef = inv(X.t()*X)*X.t()*y;
}
.
Ci vogliono circa 6 secondi per eseguire con una matrice 1000000x100
per X
. Alcuni tempi nel codice (non mostrati) indicano che tutto il tempo viene speso sul calcolo coef
.
X <- matrix(rnorm(1000000*100), ncol=100)
y <- matrix(rep(1, 1000000))
system.time(simpleLm(X,y))
user system elapsed
6.028 0.009 6.040
.
Ora considera una funzione molto simile scritta in C ++ che viene quindi compilata con g++
.
#include <iostream>
#include <armadillo>
#include <chrono>
#include <cstdlib>
using namespace std;
using namespace arma;
int main(int argc, char **argv) {
int n = 1000000;
mat X = randu<mat>(n,100);
vec y = ones<vec>(n);
chrono::steady_clock::time_point start = chrono::steady_clock::now();
colvec coef = inv(X.t()*X)*X.t()*y;
chrono::steady_clock::time_point end = chrono::steady_clock::now();
chrono::duration<double, milli> diff = end - start;
cout << diff.count() << endl;
return 0;
}
.
Qui il calcolo della variabile coef
richiede solo circa 0,5 secondi o solo 1/12 del tempo come quando è stato eseguito con RCPPARMADILLO.
Sto usando Mac OS X 10.9.2 con R 3.1.0, RCPP 0.11.1 e RCPPARMADILLO 0.4.200.0. Ho compilato l'esempio RCPP usando la funzione SourceCPP. L'esempio standalone C ++ utilizza Armadillo 4.200.0 e ho anche installato il compilatore Fortran per Mac utilizzando homebrew (brew install gfortran
).
Soluzione
GUSPIO Quick: il tuo programma nativo utilizza Blas accelerato, puoi costruire.
L'effettiva "Mattrix Math" è stata coltivata da Armadillo alla Blas Bliss.Con RCPParmadillo, ottieni ciò che è costruito contro.Con un programma nativo, forse usi qualcos'altro.Potrebbe essere semplice come il tuo programma per utilizzare le librerie accelerate mentre r not no - non so davvero come non uso OS X.
Ma per dimostrare, sulla mia macchina (I7, Linux), i tempi sono quasi identici.
Primo, il tuo programma, inalterato:
edd@max:/tmp$ g++ -std=c++11 -O3 -o abiel abiel.cpp -larmadillo -llapack
edd@max:/tmp$ ./abiel
2454
edd@max:/tmp$
.
In secondo luogo, il tuo programma avvolto in qualcosa di R può chiamare (vedi sotto):
R> library(Rcpp)
R> sourceCpp("/tmp/abielviaR.cpp")
R> abielDemo()
2354.41
[1] TRUE
R>
.
circa lo stesso.
Il codice di abielviaR.cpp
segue.
#include <RcppArmadillo.h>
#include <chrono>
using namespace std;
using namespace arma;
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::export]]
bool abielDemo() {
int n = 1000000;
mat X = randu<mat>(n,100);
vec y = ones<vec>(n);
chrono::steady_clock::time_point start = chrono::steady_clock::now();
colvec coef = inv(X.t()*X)*X.t()*y;
chrono::steady_clock::time_point end = chrono::steady_clock::now();
chrono::duration<double, milli> diff = end - start;
Rcpp::Rcout << diff.count() << endl;
return true;
}
.
PS Non dovresti davvero calcolare OLS VIA (X'X) ^ (- 1) X però.