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
Ö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 ixär större än noll, addera ett tilln_positive_elements. - Efter den loopen allokerar du en numerisk vektor,
positive_x, med storlekenn_positive_elements. - I den andra
for-loopen kontrollerar du igen om det i:te elementet ixär större än noll. - När det är det, tilldelar du det j:te elementet i
positive_xvärdet från det i:te elementet ixoch adderar sedan ett tillj.
- I den första
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)
)
*/