Nie zmieniaj rozmiaru wektorów Rcpp
Klasy wektorów Rcpp są zaprojektowane jako bardzo cienkie opakowania wokół wektorów R. Ma to istotny wpływ na wydajność: powiększanie lub zmniejszanie wektora wymaga utworzenia nowego wektora o odpowiednim rozmiarze i skopiowania do niego danych. To bardzo kosztowna operacja, której warto unikać.
Jeśli to możliwe, warto najpierw obliczyć docelowy rozmiar wektora, a dopiero potem go zaalokować z tym rozmiarem.
Spójrz na przykład, który wybiera wartości dodatnie z wektora – odpowiednik x[x > 0] w R. Ponieważ z góry nie wiadomo, ile liczb dodatnich się pojawi, kuszące jest zaczęcie od wektora o zerowej długości i dołączanie kolejnych wartości za pomocą push_back().
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;
}
Niestety ta funkcja działa wolno, bo wielokrotnie tworzy nowe wektory i kopiuje dane. Spróbuj napisać lepszą wersję!
To ćwiczenie jest częścią kursu
Optymalizacja kodu R za pomocą Rcpp
Instrukcje do ćwiczenia
- Uzupełnij definicję wydajniejszej funkcji
good_select_positive_values_cpp(), która wybiera liczby dodatnie.- W pierwszej pętli
forsprawdź, czy i-ty elementxjest większy od zera – jeśli tak, dodaj jeden don_positive_elements. - Po tej pętli zaalokuj wektor numeryczny
positive_xo rozmiarzen_positive_elements. - W drugiej pętli
forponownie sprawdź, czy i-ty elementxjest większy od zera. - Jeśli tak, przypisz j-temu elementowi
positive_xwartość i-tego elementux, a następnie dodaj jeden doj.
- W pierwszej pętli
- Funkcja
bad_select_positive_values_cpp()jest dostępna w twoim środowisku pracy do porównania. Przeanalizuj wyniki w konsoli, aby zobaczyć zmierzony czas wykonania obu wersji.
Interaktywne ćwiczenie praktyczne
Spróbuj tego ćwiczenia, uzupełniając ten przykładowy kod.
#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)
)
*/