C ++ 및 R에 대한 경험이 있지만 Rcpp의 초보자입니다. 최근에 저는 이전 프로젝트에서 Rcpp를 사용하여 큰 성공을 거두었으므로 새로운 프로젝트에 적용하기로 결정했습니다. 내 Rcpp 코드가 해당 R 함수보다 훨씬 느릴 수 있다는 사실에 놀랐습니다. 이유를 파악하기 위해 R 기능을 단순화하려고 시도했지만 단서를 찾을 수 없습니다. 귀하의 도움과 의견을 환영합니다!
R 및 Rcpp 구현을 비교하는 주요 R 함수 :
main <- function(){
n <- 50000
Delta <- exp(rnorm(n))
delta <- exp(matrix(rnorm(n * 5), nrow = n))
rx <- matrix(rnorm(n * 20), nrow = n)
print(microbenchmark(c1 <- test(Delta, delta, rx), times = 500))
print(microbenchmark(c2 <- rcpp_test(Delta, delta, rx), times = 500))
identical(c1, c2)
list(c1 = c1, c2 = c2)
}
R 구현 :
test <- function(Delta, delta, rx){
const <- list()
for(i in 1:ncol(delta)){
const[[i]] <- rx * (Delta / (1 + delta[, i]))
}
const
}
Rcpp 구현 :
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
List rcpp_test(NumericVector Delta,
NumericMatrix delta,
NumericMatrix rx) {
int n = Delta.length();
int m = rx.ncol();
List c;
NumericMatrix c1;
for(int i = 0; i < delta.ncol(); ++i){
c1 = NumericMatrix(n, m);
for(int k = 0; k < n; ++k){
double tmp = Delta[k] / (1 + delta(k, i));
for(int j = 0; j < c1.ncol(); ++j){
c1(k, j) = rx(k, j) * tmp;
}
}
c.push_back(c1);
}
return c;
}
Rcpp를 사용한다고해서 효율성이 향상된다는 보장이 없다는 것을 알고 있지만 여기에 표시된 간단한 예제를 보면 Rcpp 코드가 왜 그렇게 느리게 실행되는지 알 수 없습니다.
Unit: milliseconds
expr min lq mean median uq max neval
c1 <- test(Delta, delta, rx) 13.16935 14.19951 44.08641 30.43126 73.78581 115.9645 500
Unit: milliseconds
expr min lq mean median uq max neval
c2 <- rcpp_test(Delta, delta, rx) 143.1917 158.7481 171.6116 163.413 173.7677 247.5495 500
이상적으로 rx
는 내 프로젝트의 행렬 목록입니다. i
for 루프 의 변수 는 계산할 요소를 선택하는 데 사용됩니다. 처음에는 a List
를 Rcpp에 전달하는 것이 높은 오버 헤드를 가질 수 있다고 생각했기 때문에이 예제에서는 rx
모든 .NET 에 사용되는 고정 행렬 이라고 가정 했습니다 i
. 그것이 느린 이유가 아닌 것 같습니다.
귀하의 R 코드는 다소 최적 인 것 같습니다. 즉, 모든 실제 작업은 컴파일 된 코드에서 수행됩니다. C ++ 코드의 경우 내가 찾을 수있는 주요 문제 c1.ncol()
는 타이트 루프에서 호출 하는 것입니다. 내가 가진 것을 교체하는 경우 m
내가 믹스에 RcppArmadillo을 추가하면 C ++ 솔루션은 거의 빨리 R.으로, 나는 매우 컴팩트 한 구문을 얻을 수 있지만 빠른 순수 Rcpp의 코드보다. 나에게 이것은 잘 작성된 R 코드를이기는 것이 정말 어려울 수 있음을 보여줍니다.
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
using namespace Rcpp;
// [[Rcpp::export]]
List arma_test(const arma::vec& Delta,
const arma::mat& delta,
const arma::mat& rx) {
int l = delta.n_cols;
List c(l);
for (int i = 0; i < l; ++i) {
c(i) = rx.each_col() % (Delta / (1 + delta.col(i)));
}
return c;
}
// [[Rcpp::export]]
List rcpp_test(NumericVector Delta,
NumericMatrix delta,
NumericMatrix rx) {
int n = Delta.length();
int m = rx.ncol();
List c(delta.ncol());
NumericMatrix c1;
for(int i = 0; i < delta.ncol(); ++i){
c1 = NumericMatrix(n, m);
for(int k = 0; k < n; ++k){
double tmp = Delta[k] / (1 + delta(k, i));
for(int j = 0; j < m; ++j){
c1(k, j) = rx(k, j) * tmp;
}
}
c(i) = c1;
}
return c;
}
/*** R
test <- function(Delta, delta, rx){
const <- list()
for(i in 1:ncol(delta)){
const[[i]] <- rx * (Delta / (1 + delta[, i]))
}
const
}
n <- 50000
Delta <- exp(rnorm(n))
delta <- exp(matrix(rnorm(n * 5), nrow = n))
rx <- matrix(rnorm(n * 20), nrow = n)
bench::mark(test(Delta, delta, rx),
arma_test(Delta, delta, rx),
rcpp_test(Delta, delta, rx))
*/
산출:
# A tibble: 3 x 14
expression min mean median max `itr/sec` mem_alloc n_gc n_itr
<chr> <bch:t> <bch:t> <bch:t> <bch:t> <dbl> <bch:byt> <dbl> <int>
1 test(Delt… 84.3ms 85.2ms 84.9ms 86.6ms 11.7 44.9MB 2 4
2 arma_test… 106.5ms 107.7ms 107.7ms 108.9ms 9.28 38.1MB 3 2
3 rcpp_test… 101.9ms 103.2ms 102.2ms 106.6ms 9.69 38.1MB 1 4
# … with 5 more variables: total_time <bch:tm>, result <list>, memory <list>,
# time <list>, gc <list>
또한 출력 목록을를 피하는 필수 크기로 명시 적으로 초기화 push_back
했지만 큰 차이는 없습니다. Rcpp의 데이터 구조와 같은 벡터를 사용하면 벡터 push_back
를 확장 할 때마다 복사본이 만들어 지므로 사용을 피해야 합니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다