Rcpp ベクトルのサイズは変更しないでください
Rcpp のベクトルクラスは、R のベクトルをほぼそのまま薄く包んだラッパーとして設計されています。つまり、サイズを増減させると、その都度、適切なサイズの新しいベクトルを作成し、関連データをコピーする必要があります。これは非常に遅い処理なので、避けるべきです。
可能であれば、まず最終的なベクトルのサイズを計算してから、そのサイズでメモリを確保するようにコードを組み立ててください。
ここでは、R の x[x > 0] と同等の、ベクトルから正の値だけを選ぶ例を見てみましょう。事前に正の数がいくつあるか分からないため、要素数 0 のベクトルから始めて、見つけるたびに値を末尾に追加したくなるかもしれません。ここでの 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;
}
しかし、この関数はそのたびに新しいベクトルを作ってデータをコピーするため、遅くなってしまいます。もっと良い方法を考えてみましょう!
この演習はコースの一部です
Rcpp で R コードを最適化する
演習の手順
- より効率的な関数
good_select_positive_values_cpp()の定義を完成させ、正の数だけを選択してください。- 最初の
forループでは、xの i 番目の要素が 0 より大きければ、n_positive_elementsに 1 を加えます。 - そのループの後、サイズが
n_positive_elementsの数値ベクトルpositive_xを確保します。 - 2 つ目の
forループでも、xの i 番目の要素が 0 より大きいかを再度確認します。 - 条件を満たしたら、
positive_xの j 番目の要素をxの i 番目の要素に設定し、その後でjに 1 を加えます。
- 最初の
- 比較のために
bad_select_positive_values_cpp()がワークスペースに用意されています。コンソール出力を確認して、実行時間のベンチマーク差を見てみましょう。
実践的なインタラクティブ演習
このサンプルコードを完成させて、この演習に挑戦してみましょう。
#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)
)
*/