Kom igångKom igång gratis

Undvik att ändra storleken på Rcpp-vektorer

Rcpp-vektorklasser är utformade som tunna omslag runt R-vektorer. Det innebär att det är kostsamt att utöka eller krympa dem – varje gång måste en ny vektor av rätt storlek skapas och all relevant data kopieras. Det är en långsam operation som bör undvikas.

Om möjligt bör du strukturera koden så att du först beräknar vektorns slutliga storlek och sedan allokerar den med den storleken.

Låt oss titta på ett exempel som väljer ut de positiva värdena från en vektor, motsvarande x[x > 0] i R. Eftersom du inte vet i förväg hur många positiva tal det finns kan det verka naturligt att börja med en tom vektor och lägga till ett värde varje gång du hittar ett. Här är push_back() en funktion som lägger till ett värde i slutet av vektorn.

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

Dessvärre är den här funktionen långsam, eftersom den upprepade gånger skapar nya vektorer och kopierar data. Se om du kan göra det bättre!

Den här övningen är en del av kursen

Optimera R-kod med Rcpp

Visa kurs

Övningsinstruktioner

  • Slutför definitionen av den mer effektiva funktionen good_select_positive_values_cpp() för att välja ut de positiva talen.
    • I den första for-loopen: om det i:te elementet i x är större än noll, addera ett till n_positive_elements.
    • Efter den loopen allokerar du en numerisk vektor, positive_x, med storleken n_positive_elements.
    • I den andra for-loopen kontrollerar du igen om det i:te elementet i x är större än noll.
    • När det är det, tilldelar du det j:te elementet i positive_x värdet från det i:te elementet i x och adderar sedan ett till j.
  • bad_select_positive_values_cpp() finns tillgänglig i din arbetsmiljö för jämförelse. Granska konsolens utdata för att se den uppmätta skillnaden i körtid.

Interaktiv övning med praktiskt arbete

Testa den här övningen genom att slutföra den här exempelkoden.

#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)
)
*/
Redigera och kör kod