ループをベクトル化された操作に変換する方法がわかりません。ただし、パフォーマンスを大幅に向上させるためのかなり簡単なオプションが 2 つあります。1 つ目は、単純にループをR
関数に入れ、compiler
パッケージを使用してそれをプリコンパイルすることです。もう少し複雑な 2 番目のオプションは、R
ループをループに変換c++
し、パッケージを使用してRcpp
それを関数にリンクすることR
です。次に、それを高速なコードにR
渡す関数を呼び出します。c++
これらのオプションとタイミングの両方を示します。Rcpp listserv の Alexandre Bujard の助けに感謝したいと思います。彼は、私が理解できなかったポインターの問題を解決してくれました。
まず、R
これが関数としてのループfoo.r
です。
## Your R loop as a function
foo.r <- function(d) {
ans <- d
running_total <- 100
count <- 1
max <- 100
toggle <- FALSE
processing <- FALSE
for(i in d){
if(i != 0 ){
processing <- TRUE
if(toggle == TRUE){
ans[count] <- 0
} else {
running_total = running_total + i;
if (running_total > max) {
max <- running_total
} else if (0.98*max > running_total) {
toggle <- TRUE
}
}
}
if(i == 0 && processing == TRUE) {
running_total <- 100
max <- 100
toggle <- FALSE
}
count <- count + 1
}
return(ans)
}
compiler
これで、パッケージをロードして関数をコンパイルし、それを呼び出すことができますfoo.rcomp
。
## load compiler package and compile your R loop
require(compiler)
foo.rcomp <- cmpfun(foo.r)
コンパイルルートに必要なのはそれだけです。それはすべてR
、明らかに非常に簡単です。このc++
アプローチでは、Rcpp
パッケージとinline
、コードを「インライン化」できるパッケージを使用しc++
ます。つまり、ソース ファイルを作成してコンパイルする必要はありません。ソース ファイルをR
コードに含めるだけで、コンパイルが処理されます。
## load Rcpp package and inline for ease of linking
require(Rcpp)
require(inline)
## Rcpp version
src <- '
const NumericVector xx(x);
int n = xx.size();
NumericVector res = clone(xx);
int toggle = 0;
int processing = 0;
int tot = 100;
int max = 100;
typedef NumericVector::iterator vec_iterator;
vec_iterator ixx = xx.begin();
vec_iterator ires = res.begin();
for (int i = 0; i < n; i++) {
if (ixx[i] != 0) {
processing = 1;
if (toggle == 1) {
ires[i] = 0;
} else {
tot += ixx[i];
if (tot > max) {
max = tot;
} else if (.98 * max > tot) {
toggle = 1;
}
}
}
if (ixx[i] == 0 && processing == 1) {
tot = 100;
max = 100;
toggle = 0;
}
}
return res;
'
foo.rcpp <- cxxfunction(signature(x = "numeric"), src, plugin = "Rcpp")
これで、期待どおりの結果が得られることをテストできます。
## demonstrate equivalence
d <- c(0,0,0,1,3,4,5,-1,2,3,-5,8,0,0,-2,-3,3,5,0,0,0,-1,-1,-1,-1)
all.equal(foo.r(d), foo.rcpp(d))
d
最後に、これを 10e4 回繰り返して、より大きなバージョンの を作成します。次に、純粋なR
コード、コンパイルされたR
コード、およびコードにR
リンクされた関数の 3 つの異なる関数を実行できc++
ます。
## make larger vector to test performance
dbig <- rep(d, 10^5)
system.time(res.r <- foo.r(dbig))
system.time(res.rcomp <- foo.rcomp(dbig))
system.time(res.rcpp <- foo.rcpp(dbig))
私のシステムでは、次のようになります。
> system.time(res.r <- foo.r(dbig))
user system elapsed
12.55 0.02 12.61
> system.time(res.rcomp <- foo.rcomp(dbig))
user system elapsed
2.17 0.01 2.19
> system.time(res.rcpp <- foo.rcpp(dbig))
user system elapsed
0.01 0.00 0.02
コンパイルされたコードは、250 万のベクトルを操作するのに 2 秒しかかからないコンパイルされていないR
コードの約 1/6 の時間を要します。R
このコードは、完了するのにわずか 0.02 秒しかかからないc++
コンパイル済みコードよりも桁違いに高速です。R
初期設定を除けば、基本的なループの構文はほぼ同じであるR
ためc++
、明確さを失うことさえありません。ループの一部またはすべてを でベクトル化できたとしても、 にリンクされR
た関数のパフォーマンスを打ち負かすことに苦慮することになると思います。最後に、証明のために:R
c++
> all.equal(res.r, res.rcomp)
[1] TRUE
> all.equal(res.r, res.rcpp)
[1] TRUE
異なる関数は同じ結果を返します。