CommencezCommencez gratuitement

Ne changez pas la taille des vecteurs Rcpp

Les classes de vecteurs Rcpp sont conçues comme de très minces enveloppes autour des vecteurs R. Conséquence sur la performance : les agrandir ou les rapetisser implique de créer un nouveau vecteur de la bonne taille et de copier les données pertinentes. C'est très lent, donc à éviter.

Dans la mesure du possible, vous devriez organiser 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 d'avance combien de nombres positifs il y aura, il est tentant de commencer par un vecteur de longueur zéro et d'y ajouter une valeur chaque fois que vous en trouvez une. Ici, push_back() est une fonction qui ajoute une valeur à la fin.

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 à répétition de nouveaux vecteurs et copier les données. Voyez si vous pouvez faire mieux !

Cette activité fait partie du cours

Optimiser le code R avec Rcpp

Voir le cours

Instructions de l’exercice

  • 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-ième élément de x est 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 de nouveau si le i-ième élément de x est supérieur à zéro.
    • Le cas échéant, affectez le j-ième élément de positive_x au i-ième élément de 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 mesurée du temps d'exécution.

Exercice interactif pratique

Essayez cet exercice en complétant ce code d’exemple.

#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