2013-06-17 4 views
9

Ho lavorato la mia strada attraverso Rcpp tutorial di Dirk Eddelbuettel qui:esecuzione di codice compilato C++ con Rcpp

http://www.rinfinance.com/agenda/

ho imparato come salvare un file C++ in una directory e lo chiamano ed eseguirlo da all'interno di file di R. Il C++ sono in esecuzione si chiama 'logabs2.ccp' e il suo contenuto sono direttamente da una delle diapositive di Dirk:

#include <Rcpp.h> 

using namespace Rcpp; 

inline double f(double x) { return ::log(::fabs(x)); } 

// [[Rcpp::export]] 
std::vector<double> logabs2(std::vector<double> x) { 
    std::transform(x.begin(), x.end(), x.begin(), f); 
    return x; 
} 

l'eseguo con questo codice R:

library(Rcpp) 
sourceCpp("c:/users/mmiller21/simple r programs/logabs2.cpp") 
logabs2(seq(-5, 5, by=2)) 
# [1] 1.609438 1.098612 0.000000 0.000000 1.098612 1.609438 

Sto eseguendo il codice su un computer Windows 7 dall'interno della GUI R che sembra installare per impostazione predefinita. Ho anche installato la versione più recente di Rtools. Il codice R sopra riportato sembra richiedere un tempo relativamente lungo per essere eseguito. Sospetto che la maggior parte di quel tempo sia dedicata alla compilazione del codice C++ e che una volta compilato il codice C++ viene eseguito molto rapidamente. Microbenchmark suggerisce certamente che Rcpp riduce il tempo di calcolo.

Fino ad ora non ho mai usato il C++, ma so che quando compilo il codice C ottengo un file * .exe. Ho cercato il mio disco rigido da un file chiamato logabs2.exe ma non riesco a trovarne uno. Mi chiedo se il suddetto codice C++ potrebbe essere eseguito anche più rapidamente se è stato creato un file logabs2.exe. È possibile creare un file logabs2.exe e memorizzarlo in una cartella da qualche parte e quindi chiamare Rcpp quel file ogni volta che volevo usarlo? Non so se abbia senso. Se potessi memorizzare una funzione C++ in un file * .exe, forse non dovrei compilare la funzione ogni volta che volessi usarla con Rcpp e forse il codice Rcpp sarebbe ancora più veloce.

Ci scusiamo se questa domanda non ha senso o è un duplicato. Se è possibile memorizzare la funzione C++ come file * .exe, spero che qualcuno mi mostrerà come modificare il mio codice R sopra per eseguirlo. Grazie per l'aiuto con questo o per avermi fatto capire perché ciò che suggerisco non è possibile o consigliato.

Non vedo l'ora di vedere il nuovo libro di Dirk.

+2

Hai guardato il comando 'R CMD SHLIB'? Con ciò puoi semplicemente compilare la tua funzione C++ in un file dll e quindi caricare il file compilato con 'dyn.load()'. Dai un'occhiata a '? SHLIB' e'? Dyn.load' per i dettagli! – user1981275

+0

@ user1981275 Grazie per il commento. Finora sono arrivato a questo punto: C: \ Programmi \ R \ R-3.0.1 \ bin \ x64> rcmd SHLIB -oc: \ users \ mmiller21 \ documents \ r \ win-library \ 3.0 \ rcpp \ include \ logabs2.dll c: \ users \ mmiller21 \ documents \ r \ win-library \ 3.0 \ rcpp \ include \ logabs2.cpp Ricevo il seguente messaggio: make: *** Nessuna regola per rendere target 'c: \ users \ mmiller21 \ documents \ r \ win-library \ 3.0 \ rcpp \ include \ logabs2.o ', necessario per' c: \ users \ mmiller21 \ documents \ r \ win-library \ 3.0 \ rcpp \ include \ logabs2.dll '. Stop. Il messaggio sembra dire che mi manca un file oggetto, a me familiare dal mio lavoro con C. –

+0

Sto usando la finestra di comando DOS. Se il problema è davvero un file oggetto mancante puoi suggerire come posso creare un file oggetto? Forse una volta creato verrà inserito in un elenco insieme a logabs2.cpp alla fine del comando nel commento precedente? Grazie a prescindere. –

risposta

4

Tu dici

Non ho mai usato C++ fino ad ora, ma so che quando compilo il codice C ottengo un file * .exe

e questo è vero se e solo si crea un eseguibile . Qui, costruiamo librerie caricabili dinamicamente e quelli thend avere extensionos diverse a seconda del sistema operativo: DLL per Windoze, .so per Linux, .dynlib per OS X.

Quindi niente di sbagliato qui, è semplicemente dovuto l'assunzione sbagliata.

1

Se si desidera ottenere qualche entità che è possibile mantenere, ciò che si sta cercando è un pacchetto R. Ci sono molte risorse online per imparare come realizzarle (ad esempio Hadley's slides).

Abbiamo Rcpp.package.skeleton che potresti trovare utile.

Quindi, la funzione viene compilata una sola volta quando il pacchetto è installato e quindi lo si utilizza.

6

Grazie a user1981275, Dirk Eddelbuettel e Romain Francois per le loro risposte. Di seguito è come ho compilato un file C++ e creato un * .dll, poi chiamato e utilizzato quel file * .dll all'interno di R.

Passaggio 1. Ho creato una nuova cartella denominata "c: \ users \ mmiller21 \ myrpackages" e incollato il file "logabs2.cpp" nella nuova cartella. Il file 'logabs2.cpp' è stato creato come descritto nel mio post originale.

Passo 2. All'interno della nuova cartella ho creato un nuovo pacchetto R chiamato "logabs2" utilizzando un file R che ho scritto chiamato "nuovo pacchetto creation.r". Il contenuto di 'nuovo pacchetto creation.r' sono:

setwd('c:/users/mmiller21/myrpackages/') 

library(Rcpp) 

Rcpp.package.skeleton("logabs2", example_code = FALSE, cpp_files = c("logabs2.cpp")) 

ho trovato la sintassi sopra per Rcpp.package.skeleton su uno dei siti web di Hadley Wickham: https://github.com/hadley/devtools/wiki/Rcpp

Fase 3. Ho installato la nuova R pacchetto "logabs2" nel R utilizzando la seguente riga nella finestra di comando DOS:

C:\Program Files\R\R-3.0.1\bin\x64>R CMD INSTALL -l c:\users\mmiller21\documents\r\win-library\3.0\ c:\users\mmiller21\myrpackages\logabs2 

dove:

la posizione del file Rcmd.exe è:

C:\Program Files\R\R-3.0.1\bin\x64> 

la posizione del installati R pacchetti sul mio computer è:

c:\users\mmiller21\documents\r\win-library\3.0\ 

e la posizione del mio nuovo pacchetto R prima di essere installato è:

c:\users\mmiller21\myrpackages\ 

sintassi utilizzata nella finestra di comando DOS è stato trovato per tentativi ed errori e non può essere l'ideale. Ad un certo punto ho incollato una copia di "logabs2.cpp" in "C: \ Programmi \ R \ R-3.0.1 \ bin \ x64>" ma non penso che sia importante.

Punto 4. Dopo aver installato il nuovo pacchetto R ho eseguito utilizzando un file R ho chiamato 'nuovo pacchetto usage.r' in la 'c:/utenti/mmiller21/myrpackages /' cartella (anche se io non credo che il la cartella era importante). Il contenuto di 'nuovo pacchetto usage.r' sono:

library(logabs2) 
logabs2(seq(-5, 5, by=2)) 

L'uscita era:

# [1] 1.609438 1.098612 0.000000 0.000000 1.098612 1.609438 

Questo file caricato il pacchetto Rcpp senza chiedermi.

In questo caso la base R era più veloce supponendo che l'ho fatto correttamente.

#> microbenchmark(logabs2(seq(-5, 5, by=2)), times = 100) 
#Unit: microseconds 
#      expr min  lq median  uq  max neval 
# logabs2(seq(-5, 5, by = 2)) 43.086 44.453 50.6075 69.756 190.803 100 

#> microbenchmark(log(abs(seq(-5, 5, by=2))), times=100) 
#Unit: microseconds 
#       expr min  lq median uq  max neval 
# log(abs(seq(-5, 5, by = 2))) 38.298 38.982 39.666 40.35 173.023 100 

Tuttavia, utilizzando il file dll è stato più veloce di chiamare il file cpp esterna:

system.time(

cppFunction(" 
NumericVector logabs(NumericVector x) { 
    return log(abs(x)); 
} 
") 

) 

# user system elapsed 
# 0.06 0.08 5.85 

Anche se di base R sembra più veloce o veloce come il * .dll file in questo caso, non ho dubito che l'uso del file * .dll con Rcpp sarà più veloce della base R nella maggior parte dei casi.

Questo è stato il mio primo tentativo di creare un pacchetto R o l'utilizzo di Rcpp e senza dubbio non ho usato i metodi più efficienti. Inoltre, mi scuso per eventuali errori tipografici in questo post.

EDIT

In un commento qui sotto penso Romain Francois suggerito modifico il file cpp * al seguente:

#include <Rcpp.h> 
using namespace Rcpp; 

// [[Rcpp::export]] 

NumericVector logabs(NumericVector x) { 
return log(abs(x)); 
} 

e ricreare il mio pacchetto R, che ora ho fatto. Ho poi confrontato base di R contro il mio nuovo pacchetto utilizzando il seguente codice:

library(logabs) 

logabs(seq(-5, 5, by=2)) 
log(abs(seq(-5, 5, by=2))) 

library(microbenchmark) 

microbenchmark(logabs(seq(-5, 5, by=2)), log(abs(seq(-5, 5, by=2))), times = 100000) 

Base R è ancora un pochino più veloce o non è diverso:

Unit: microseconds 
         expr min  lq median  uq  max neval 
    logabs(seq(-5, 5, by = 2)) 42.401 45.137 46.505 69.073 39754.598 1e+05 
log(abs(seq(-5, 5, by = 2))) 37.614 40.350 41.718 62.234 3422.133 1e+05 

Forse perché di base R è già vettorializzare. Sospetto che con funzioni più complesse la base R sarà molto più lenta. O forse non sto ancora utilizzando l'approccio più efficiente, o forse ho semplicemente fatto un errore da qualche parte.

+0

L'uso di 'std :: vector ' o 'NumericVector' non è la stessa cosa. 'lobabs2' prima deve convertire l'oggetto R in un' std :: vector ', questo è costoso in quanto è necessario copiare i dati. Quindi alla fine si restituisce un 'std :: vector ', quindi 'Rcpp' deve trasformarlo in un oggetto R, ancora una volta significa la copia dei dati. –

+0

Nel tuo ultimo blocco di codice, stai misurando il tempo necessario per compilare il codice. Questo non è un paragone giusto. –

+0

@Romain Francois Grazie per i vostri commenti. Non sto cercando di essere ingiusto. Semplicemente non ho ancora imparato abbastanza per fornire una risposta più efficiente. Non sono ancora riuscito ad ottenere dyn.load e .Call per funzionare, ad esempio, dopo aver creato il file * .dll con Rcpp.package.skeleton e/o installato il nuovo pacchetto. Né sono stato ancora in grado di creare il file * .dll usando SHLIB. Se arrivo a una soluzione più efficiente, lo posterò. Il mio obiettivo è imparare a massimizzare l'utilità di Rcpp. –