Neměň velikost Rcpp vektorů
Třídy Rcpp vektorů jsou navrženy jako velmi tenké obálky kolem R vektorů. Z toho plyne výkonnostní důsledek: zvětšení nebo zmenšení vektoru znamená vytvoření nového vektoru správné velikosti a zkopírování příslušných dat. Jde o velmi pomalou operaci, které je lepší se vyhnout.
Kdykoli je to možné, je vhodné kód strukturovat tak, aby se nejprve vypočítala výsledná velikost vektoru a teprve pak se alokoval vektor s touto velikostí.
Podívejme se na příklad, který vybere kladné hodnoty z vektoru — to odpovídá výrazu x[x > 0] v R. Protože dopředu nevíme, kolik kladných čísel bude, může být lákavé začít s vektorem nulové délky a pokaždé, když takové číslo najdeme, přidat ho na konec. K tomu slouží funkce 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;
}
Tato funkce bude bohužel pomalá, protože opakovaně vytváří nové vektory a kopíruje data. Zkus to udělat lépe!
Toto cvičení je součástí kurzu
Optimizing R Code with Rcpp
Pokyny k cvičení
- Dokonči definici efektivnější funkce
good_select_positive_values_cpp(), která vybere kladná čísla.- V prvním cyklu
forzkontroluj, jestli je i-tý prvek vektoruxvětší než nula — pokud ano, přičti jedničku kn_positive_elements. - Po tomto cyklu alokuj numerický vektor
positive_xo velikostin_positive_elements. - Ve druhém cyklu
forznovu zkontroluj, jestli je i-tý prvek vektoruxvětší než nula. - Pokud ano, nastav j-tý prvek vektoru
positive_xna i-tý prvek vektoruxa přičti jedničku kj.
- V prvním cyklu
- Pro srovnání máš v pracovním prostředí k dispozici funkci
bad_select_positive_values_cpp(). Prohlédni si výstup konzole a zjisti, jaký je mezi nimi rozdíl v době běhu.
Interaktivní cvičení na vyzkoušení si v praxi
Vyzkoušejte si toto cvičení dokončením tohoto ukázkového kódu.
#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)
)
*/