Verander de grootte van Rcpp-vectors niet
Rcpp-vectorclasses zijn ontworpen als heel dunne wrappers rond R-vectors. Dat heeft als prestatiegevolg dat ze laten groeien of krimpen betekent dat er een nieuwe vector met de juiste grootte wordt aangemaakt en de relevante data wordt gekopieerd. Dat is erg traag en moet je dus vermijden.
Als het kan, structureer je je code zo dat je eerst de uiteindelijke grootte van de vector bepaalt en deze daarna met die grootte alloceert.
Laten we een voorbeeld bekijken dat de positieve waarden uit een vector selecteert, gelijk aan x[x > 0] in R. Omdat je niet van tevoren weet hoeveel positieve getallen er zullen zijn, is het verleidelijk om te beginnen met een vector van lengte nul en elke keer dat je er een vindt, een waarde toe te voegen. Hier is push_back() een functie die een waarde achteraan toevoegt.
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;
}
Helaas is deze functie traag omdat er herhaaldelijk nieuwe vectors moeten worden gemaakt en de data gekopieerd moet worden. Kijk of jij het beter kunt doen!
Deze oefening maakt deel uit van de cursus
R-code optimaliseren met Rcpp
Oefeninstructies
- Maak de definitie af van een efficiëntere functie,
good_select_positive_values_cpp(), om de positieve getallen te selecteren.- In de eerste
for-lus: als het i-de element vanxgroter is dan nul, tel dan één op bijn_positive_elements. - Alloceer na die for-lus een numerieke vector,
positive_x, met grootten_positive_elements. - Controleer in de tweede
for-lus opnieuw of het i-de element vanxgroter is dan nul. - Als dat zo is, zet dan het j-de element van
positive_xgelijk aan het i-de element vanxen tel daarna één op bijj.
- In de eerste
bad_select_positive_values_cpp()is beschikbaar in je werkruimte ter vergelijking. Bekijk de console-uitvoer om het gebenchmarkt verschil in looptijd te zien.
Praktische interactieve oefening
Probeer deze oefening eens door deze voorbeeldcode in te vullen.
#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)
)
*/