Zacznij terazZacznij za darmo

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

Zobacz kurs

Instrukcje do ćwiczenia

  • Uzupełnij definicję wydajniejszej funkcji good_select_positive_values_cpp(), która wybiera liczby dodatnie.
    • W pierwszej pętli for sprawdź, czy i-ty element x jest większy od zera – jeśli tak, dodaj jeden do n_positive_elements.
    • Po tej pętli zaalokuj wektor numeryczny positive_x o rozmiarze n_positive_elements.
    • W drugiej pętli for ponownie sprawdź, czy i-ty element x jest większy od zera.
    • Jeśli tak, przypisz j-temu elementowi positive_x wartość i-tego elementu x, a następnie dodaj jeden do j.
  • 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)
)
*/
Edytuj i uruchom kod