CommencerCommencer gratuitement

Ne modifiez pas la taille des vecteurs Rcpp

Les classes de vecteurs Rcpp sont conçues comme de très fines couches autour des vecteurs R. Conséquence en termes de performance : les agrandir ou les réduire revient à créer un nouveau vecteur de la bonne taille et à copier les données pertinentes. C’est très lent, donc à éviter.

Dans la mesure du possible, structurez votre code pour d’abord calculer la taille finale du vecteur, puis l’allouer avec cette taille.

Voyons un exemple qui sélectionne les valeurs positives d’un vecteur, équivalent à x[x > 0] en R. Comme vous ne savez pas à l’avance combien de nombres positifs il y aura, il est tentant de commencer avec un vecteur de longueur nulle et d’y ajouter une valeur à chaque fois que vous en trouvez une. Ici, push_back() est une fonction qui ajoute une valeur en fin de vecteur.

NumericVector bad_select_positive_values_cpp(NumericVector x) {
  NumericVector positive_x(0);
  for(int i = 0; i < x.size(); i++) {
    if(x[i] > 0) {
      positive_x.push_back(x[i]);
    }
  }
  return positive_x;
}

Malheureusement, cette fonction sera lente car elle doit créer de nouveaux vecteurs à plusieurs reprises et copier les données. Voyez si vous pouvez faire mieux !

Cet exercice fait partie du cours

Optimiser du code R avec Rcpp

Afficher le cours

Instructions

  • Complétez la définition d’une fonction plus efficace, good_select_positive_values_cpp() pour sélectionner les nombres positifs.
    • Dans la première boucle for, si le i-ème élément de x est strictement supérieur à zéro, ajoutez 1 à n_positive_elements.
    • Après cette boucle, allouez un vecteur numérique, positive_x, de taille n_positive_elements.
    • Dans la deuxième boucle for, vérifiez à nouveau si le i-ème élément de x est strictement supérieur à zéro.
    • Le cas échéant, affectez le i-ème élément de x au j-ième élément de positive_x, puis ajoutez 1 à j.
  • bad_select_positive_values_cpp() est disponible dans votre espace de travail pour comparaison. Examinez la sortie de la console pour voir la différence de temps d’exécution mesurée.

Exercice interactif pratique

Essayez cet exercice en complétant cet exemple de code.

#include 
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector good_select_positive_values_cpp(NumericVector x) {
  int n_elements = x.size();
  int n_positive_elements = 0;
  
  // Calculate the size of the output
  for(int i = 0; i < n_elements; i++) {
    // If the ith element of x is positive
    if(___) {
      // Add 1 to n_positive_elements
      ___;
    }
  }
  
  // Allocate a vector of size n_positive_elements
  ___;
  
  // Fill the vector
  int j = 0;
  for(int i = 0; i < n_elements; i++) {
    // If the ith element of x is positive
    if(___) {
      // Set the jth element of positive_x to the ith element of x
      ___;
      // Add 1 to j
      ___;
    }
  }
  return positive_x;
}

/*** R
set.seed(42)
x <- rnorm(1e4)
# Does it give the same answer as R?
all.equal(good_select_positive_values_cpp(x), x[x > 0])
# Which is faster?
microbenchmark(
  bad_cpp = bad_select_positive_values_cpp(x),
  good_cpp = good_select_positive_values_cpp(x)
)
*/
Modifier et exécuter le code