Où puis-je apprendre à écrire du code C pour accélérer les fonctions R lentes? [fermé]

Quelle est la meilleure ressource pour apprendre à écrire du code C pour une utilisation avec R? Je connais la sectionSystem et les interfaces en langues étrangères des extensions R, mais je trouve ça assez difficile. Quelles sont les bonnes ressources (en ligne et hors ligne) pour écrire du code C à utiliser avec R?

Pour clarifier, Je ne veux pas apprendre à écrire du code C, je veux apprendre à mieux intégrer R et C. par exemple, comment convertir un vecteur entier C en un vecteur entier R (ou vice versa) ou d'un scalaire C à un vecteur R?

103
demandé sur Jaap 2010-11-05 16:20:26

4 réponses

Eh bien, il y a le bon vieux Utilisez la source, Luke! - - - R lui-même a beaucoup de code C (très efficace) que L'on peut étudier, et CRAN a des centaines de paquets, certains d'auteurs en qui vous avez confiance. Cela fournit des exemples réels et testés à étudier et à adapter.

Mais comme Josh le soupçonnait, je penche plus vers C++ et donc Rcpp . Il a aussi beaucoup d'exemples.

Edit: Il y avait deux livres que j'ai trouvés utiles:

  • le premier est celui de Venables et Ripley " s Programming " même si ça devient long dans la dent (et il y a eu des rumeurs d'une 2ème édition depuis des années). À l'époque il n'y avait tout simplement rien d'autre.
  • le second dans "Software for Data Analysis" de Chambers qui est beaucoup plus récent et a une sensation beaucoup plus agréable centrée sur R-et deux chapitres sur l'extension de R. C et c++ sont mentionnés. De plus, John me déchire pour ce que j'ai fait avec digest de sorte que seul vaut le prix de admission.

Cela dit, John aime de plus en plus Rcpp (et contribue) car il trouve que la correspondance entre les objets R et les objets c++ (via Rcpp) est très naturelle - et ReferenceClasses y aide.

Edit 2: avec la question recentrée de Hadley, Je très fortement vous invite à considérer C++. Il y a tellement de bêtises que vous avez à faire avec C - - - très fastidieux et très évitable . Jetez un oeil à la Rcpp-introduction vignette . Un autre exemple simple est cet article de blog où je montre qu'au lieu de se soucier des différences de 10% (dans L'un des exemples de Radford Neal), nous pouvons obtenir huit fois augmente avec C++ (sur ce qui est bien sûr un exemple artificiel).

Edit 3: Il y a de la complexité en ce sens que vous pouvez rencontrer des erreurs c++ qui sont, pour le moins, difficiles à grok. Mais juste utiliser Rcpp plutôt que de l'étendre, vous devriez presque jamais besoin. Et tout ce Le coût est indéniable, il est de loin éclipsé par le Avantage de code plus simple, moins Standard, Pas de protection / UNPROTECT, pas de gestion de la mémoire etc pp. Doug Bates a déclaré hier qu'il trouvait que C++ et Rcpp ressemblaient beaucoup plus à écrire R qu'à écrire C++. YMMV et tout ça.

66
répondu Dirk Eddelbuettel 2010-11-05 14:12:36

Hadley,

Vous pouvez certainement écrire du code C++ similaire au code C.

Je comprends ce que vous dites à propos de C++ étant plus compliqué que C. c'est si vous voulez tout maîtriser : objets, templates, STL, template meta programming, etc... la plupart des gens n'ont pas besoin de ces choses et peuvent simplement compter sur les autres. La mise en œuvre de Rcpp est très compliquée, mais juste parce que vous ne savez pas comment fonctionne Votre Réfrigérateur, cela ne signifie pas que vous ne pouvez pas ouvrir la porte et saisir le lait frais ...

De vos nombreuses contributions à R, Ce Qui me frappe, c'est que vous trouvez R un peu fastidieux (manipulation de données, graphiques, manipulatio de chaîne, etc...). Eh bien, préparez-vous à beaucoup plus de surprises avec L'API C interne de R. C'est très fastidieux.

De temps en temps, je lis les manuels R-exts ou R-ints. Cela aide. Mais la plupart du temps, quand je veux vraiment savoir quelque chose, je vais dans la source R, et aussi dans la source des paquets écrits par exemple Simon (il y a généralement beaucoup à apprendre là-bas).

Rcpp est conçu pour faire disparaître ces aspects fastidieux de L'API.

Vous pouvez juger par vous-même ce que vous trouvez plus compliqué, obscurci, etc... basée sur quelques exemples. Cette fonction crée un vecteur de caractères à l'aide de 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);
}

En utilisant Rcpp, vous pouvez écrire la même fonction que:

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

Ou:

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

Comme Dirk l'a dit, il y a d'autres exemples sur les différentes vignettes. Nous pointons aussi généralement les gens vers nos tests unitaires parce que chacun d'eux teste une partie très spécifique du code et est un peu explicite.

Je suis évidemment biaisé ici, mais je recommanderais de me familiariser avec Rcpp au lieu d'apprendre l'API C de R, puis de venir à la liste de diffusion si quelque chose n'est pas clair ou ne semble pas faisable avec Rcpp.

De toute façon, fin de l'argumentaire de vente.

Je suppose que tout dépend du type de code que vous voulez écrire éventuellement.

Romain

52
répondu Romain Francois 2010-11-05 14:47:20

@hadley: malheureusement, je n'ai pas de ressources spécifiques en tête pour vous aider à démarrer sur C++. Je l'ai ramassé dans les livres de Scott Meyers (efficace c++, plus efficace c++, etc...) mais ce ne sont pas vraiment ce que l'on pourrait appeler l'introduction.

Nous utilisons presque exclusivement le.Interface d'appel pour appeler le code C++. La règle est assez facile :

  • la fonction C++ doit renvoyer un objet R. Tous les objets R sont SEXP.
  • la fonction C++ prend entre 0 et 65 R objets en entrée (encore SEXP)
  • , il doit (pas vraiment, mais on peut l'enregistrer pour plus tard) être déclaré avec une liaison C, soit avec extern "C" ou RcppExport alias Rcpp définit.

Donc un .La fonction d'appel est déclarée comme ceci dans un fichier d'en-tête:

#include <Rcpp.h>

RcppExport SEXP foo( SEXP x1, SEXP x2 ) ;

Et mis en œuvre comme ceci dans un .fichier cpp :

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

Il n'y a pas beaucoup plus à savoir sur L'API R pour utiliser Rcpp.

La plupart des gens veulent seulement traiter avec numeric vecteurs dans Rcpp. Vous faites cela avec la classe NumericVector. Il existe plusieurs façons de créer un vecteur numérique :

À partir d'un objet existant que vous transmettez depuis R:

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

Avec des valeurs données en utilisant la fonction:: create static:

 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
 ) ;

D'une taille donnée:

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

Ensuite, une fois que vous avez un vecteur, le plus utile est d'en extraire un élément. Ceci est fait avec l'opérateur [], avec une indexation basée sur 0, donc par exemple la somme des valeurs d'un vecteur numérique va quelque chose comme ceci:

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 ) ;
}

Mais avec Rcpp sugar nous pouvons le faire beaucoup plus bien maintenant:

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

Comme je l'ai déjà dit, tout dépend du type de code que vous voulez écrire. Regardez ce que les gens font dans les paquets qui s'appuient sur Rcpp, vérifiez les vignettes, les tests unitaires, revenez nous sur la liste de diffusion. Nous sommes toujours heureux d'aider.

28
répondu Romain Francois 2010-11-08 12:32:46

@jbremnant: C'est vrai. Les classes Rcpp implémentent quelque chose de proche du modèle RAII. Lorsqu'un objet Rcpp est créé, le constructeur prend les mesures appropriées pour s'assurer que L'objet R sous-jacent (SEXP) est protégé du garbage collector. Le destructeur retire la protection. Ceci est expliqué dans la vignetteRcpp-intrduction . L'implémentation sous-jacente repose sur les fonctions R API R_PreserveObject et R_ReleaseObject

Il y a en effet pénalité de performance due à l'encapsulation c++. Nous essayons de garder cela au minimum avec inlining, etc... La pénalité est faible, et quand vous prenez en compte le gain en termes de temps qu'il faut pour écrire et maintenir le code, ce n'est pas pertinent.

Appeler des fonctions R à partir de la fonction de classe Rcpp est plus lent que d'appeler directement eval avec l'api C. C'est parce que nous prenons des précautions et enveloppons l'appel de fonction dans un bloc tryCatch afin de capturer les erreurs R et de les promouvoir en C++ exceptions afin qu'elles puissent être traitées en utilisant le try/catch standard en C++.

La plupart des gens veulent utiliser des vecteurs (spécialement NumericVector), et la pénalité est très faible avec cette classe. Le répertoire examples/ConvolveBenchmarks contient plusieurs variantes de la fonction convolution notoire de R-exts et la vignette a des résultats de référence. Il s'avère que Rcpp le rend plus rapide que le code de référence qui utilise L'API R.

19
répondu Romain Francois 2010-11-08 12:12:18