Dove posso imparare come scrivere il codice C per velocizzare le funzioni di R lento? [chiuso]

StackOverflow https://stackoverflow.com/questions/4106174

  •  29-09-2019
  •  | 
  •  

Domanda

Qual è la miglior risorsa per imparare a scrivere codice C per l'uso con R? So del sistema di e lingua straniera interfacce sezione di estensioni R, ma trovo piuttosto difficile andare avanti. Quali sono buone risorse (sia online che offline) per la scrittura di codice C per l'uso con R?

Per chiarire, io non voglio imparare come scrivere codice C, voglio imparare come integrare meglio R e C. Per esempio, come faccio a convertire da un vettore C intero a un vettore R intero (o viceversa) oppure da uno scalare C per un vettore R?

È stato utile?

Soluzione

Beh c'è il buon vecchio Usa la fonte, Luke! --- R per sé ha un sacco di (molto efficiente) codice C si può studiare, e CRAN ha centinaia di pacchetti, alcuni da autori tu credi. Che fornisce reali, esempi testati per studiare e adattare.

Ma, come sospettava Josh, mi piego più verso C ++ e quindi Rcpp . Ha anche un sacco di esempi.

Modifica: Ci sono stati due libri che ho trovato utile:

  • Il primo è Venables e Ripley " s Programmazione ", anche se si sta facendo lungo nel dente (e ci sono state voci di un 2 ° edizione per anni). A quel tempo non c'era semplicemente nulla altro.
  • Il secondo in Chambers' ' Software per l'analisi dei dati ', che è molto più recente e ha una molto più bello R-centric tatto - e due capitoli sull'estensione R. Sia C e C ++ ottenere menzionati . Inoltre, John mi brandelli per quello che ho fatto con digerire in modo che da sola vale il prezzo di ammissione.

Detto questo, John è in crescita appassionato di Rcpp (e contribuire) quando trova la corrispondenza tra oggetti R e oggetti C ++ (tramite Rcpp ) di essere molto naturale - e ReferenceClasses aiuto là.

Modifica 2: Con domanda refocussed di Hadley, I molto forte vi invitiamo a prendere in considerazione C ++. C'è così tanto una sciocchezza boilerplate avete a che fare con C --- molto noioso e molto evitabile . Date un'occhiata alla Rcpp-introduzione vignetta . Un altro semplice esempio è questo post blog dove dimostro che invece di preoccuparsi di 10% differenze (in uno degli esempi Radford Neal) si possono ottenere eightyfold aumenta con C ++ (su ciò che è, naturalmente, un esempio forzato).

Modifica 3: Non v'è complessità che si può incorrere in errori di C ++ che sono, per usare un eufemismo, difficile da Grok. Ma a poco usare Rcpp , piuttosto che per estenderlo, si dovrebbe quasi mai bisogno. E mentre questo costo è innegabile, è di gran lunga eclissato dalla vantaggio di codice più semplice, meno boilerplate, nessun / disattivazione della protezione, senza la gestione della memoria, ecc pp. Doug Bates proprio ieri ha dichiarato che egli trova C ++ e Rcpp di essere molto più simile a scrivere R che scrivere C ++. YMMV e tutto il resto.

Altri suggerimenti

Hadley,

Si può sicuramente scrivere codice C ++ che è simile al codice C.

Capisco quello che dici di C ++ essere più complicato di C. Questo è se si vuole padrone di tutto: oggetti, modelli, STL, modello di programmazione meta, ecc ... la maggior parte delle persone non hanno bisogno di queste cose e può solo fare affidamento su altri ad esso. L'attuazione di Rcpp è molto complicato, ma solo perché non si sa come funziona il frigo, ma non significa che non è possibile aprire la porta e prendere il latte fresco ...

Da vostri numerosi contributi alla R, quello che mi colpisce è che trovare un po 'noioso R (la manipolazione dei dati, grafica, stringa manipulatio, ecc ...). Bene prepararsi per molte altre sorprese con la C API interna di R. Questo è molto noioso.

Di tanto in tanto, ho letto i manuali R exts o R-ints. Questo aiuta. Ma la maggior parte del tempo, quando ho voglia di scoprire qualcosa, vado in sorgente R, e anche nella fonte di pacchetti scritto da esempio Simon (di solito c'è un sacco da imparare lì).

Rcpp è progettato per rendere questi aspetti noiosi della API andare via.

È possibile giudicare voi stessi quello che si trova più complicato, offuscato, ecc ... sulla base di alcuni esempi. Questa funzione crea un vettore di caratteri utilizzando l'API C:

SEXP foobar(){
  SEXP ab;
  PROTECT(ab = allocVector(STRSXP, 2));
  SET_STRING_ELT( ab, 0, mkChar("foo") );
  SET_STRING_ELT( ab, 1, mkChar("bar") );
  UNPROTECT(1);
}

Utilizzando Rcpp, è possibile scrivere la stessa funzione:

SEXP foobar(){
   return Rcpp::CharacterVector::create( "foo", "bar" ) ;
}

o

SEXP foobar(){
   Rcpp::CharacterVector res(2) ;
   res[0] = "foo" ;
   res[1] = "bar" ;
   return res ;
}

Come Dirk ha detto, ci sono altri esempi sulle varie vignette. Noi di solito segnaliamo le persone verso i nostri test di unità, perché ciascuno di essi testare una parte molto specifica del codice e un po 'sono autoesplicativi.

Sono ovviamente prevenuto qui, ma io vi consiglio di prendere di familiare Rcpp invece di imparare l'API C di R, e poi tornare alla mailing list, se qualcosa non è chiaro o non sembra fattibile con Rcpp.

In ogni caso, fine del passo di vendite.

Credo che tutto dipende che tipo di codice che si desidera scrivere alla fine.

Romain

@hadley: purtroppo, non ho risorse specifiche in mente per aiutare a Operazioni preliminari a C ++. L'ho preso dai libri di Scott Meyers (Effective C ++, più efficace C ++, ecc ...), ma queste non sono realmente quello che si potrebbe chiamare introduttivo.

Usiamo quasi esclusivamente l'interfaccia .Call di chiamare codice C ++. La regola è abbastanza facile:

  • La funzione C ++ deve restituire un oggetto R. Tutti gli oggetti R sono sexp.
  • La funzione C ++ prende tra 0 e 65 R oggetti in ingresso (di nuovo sexp)
  • deve (non proprio, ma siamo in grado di salvare questo per dopo) essere dichiarata con collegamento C, sia con extern "C" o RcppExport alias che definisce Rcpp .

Quindi, una funzione .Call viene dichiarato come questo in qualche file di intestazione:

#include <Rcpp.h>

RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;

e implementato come questo in un file cpp:

SEXP foo( SEXP x1, SEXP x2 ){
   ...
}

Non c'è molto di più da sapere sulla API R da usando Rcpp.

La maggior parte delle persone vogliono a che fare solo con vettori numerici in Rcpp. A tale scopo, con la classe NumericVector. Ci sono diversi modi per creare un vettore numerico:

Da un oggetto esistente che si passa giù dalla R:

 SEXP foo( SEXP x_) {
    Rcpp::NumericVector x( x_ ) ;
    ...
 }

Con valori dati utilizzando il :: creare funzione statica:

 Rcpp::NumericVector x = Rcpp::NumericVector::create( 1.0, 2.0, 3.0 ) ;
 Rcpp::NumericVector x = Rcpp::NumericVector::create( 
    _["a"] = 1.0, 
    _["b"] = 2.0, 
    _["c"] = 3
 ) ;

di una data dimensione:

 Rcpp::NumericVector x( 10 ) ;      // filled with 0.0
 Rcpp::NumericVector x( 10, 2.0 ) ; // filled with 2.0

Poi, una volta che hai un vettore, la cosa più utile è quello di estrarre un elemento da esso. Questo viene fatto con l'operatore [], con indicizzazione 0-based, così per esempio sommando i valori di un vettore numerico più o meno così:

SEXP sum( SEXP x_ ){
   Rcpp::NumericVector x(x_) ;
   double res = 0.0 ;
   for( int i=0; i<x.size(), i++){
      res += x[i] ;
   }
   return Rcpp::wrap( res ) ;
}

Ma con lo zucchero Rcpp possiamo fare questo molto più bene ora:

using namespace Rcpp ;
SEXP sum( SEXP x_ ){
   NumericVector x(x_) ;
   double res = sum( x ) ;
   return wrap( res ) ;
}

Come ho detto prima, tutto dipende da che tipo di codice che si desidera scrivere. Sguardo in ciò che le persone fanno in pacchetti che si basano su Rcpp, controlla le vignette, i test di unità, tornare da noi sulla mailing list. Siamo sempre felici di aiuto.

@jbremnant: Proprio così. classi Rcpp implementare qualcosa di simile al modello Raii. Quando viene creato un oggetto Rcpp, il costruttore adotta misure adeguate per garantire il sottostante oggetto R (sexp) è protetto dal garbage collector. Il distruttore ritira la protezione. Questo è spiegato nel Rcpp-intrduction vignetta . L'implementazione sottostante si basa sulle funzioni API R R_PreserveObject e R_ReleaseObject

C'è infatti pena di prestazioni a causa di C ++ incapsulamento. Cerchiamo di mantenere questo al minimo con inlining, ecc ... La pena è piccolo, e quando si prende in considerazione il guadagno in termini di tempo necessario per scrivere e mantenere il codice, non è così rilevante.

Chiamare funzioni R dalla funzione di classe Rcpp è più lenta di chiamare direttamente eval con l'API C. Questo perché prendiamo precauzioni e avvolgere la chiamata di funzione in un blocco TryCatch in modo che gli errori di R ci cattura e li promuoviamo le eccezioni C ++ in modo che possano essere affrontati utilizzando lo standard try / catch in C ++.

La maggior parte delle persone desidera utilizzare vettori (in particolare NumericVector), e la pena è molto piccola, con questa classe. L'elenco esempi / ConvolveBenchmarks contiene diverse varianti della funzione convoluzione noto da R exts e il bollino ha risultati benchmark. Risulta che Rcpp rende più veloce del codice di riferimento che utilizza l'API R.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top