Ускорение вычислений Rcpp в цикле R

#r #loops #rcpp

#r #циклы #rcpp

Вопрос:

Хорошо известно, что реализации в Rcpp, как правило, будут намного быстрее, чем реализации в R. Меня интересует, существуют ли эффективные методы для ускорения отдельных оценок функций Rcpp, которые должны оцениваться в цикле R.

Рассмотрим следующий пример, в котором я использую простую многомерную нормальную генерирующую функцию в Rcpp:

 #include <RcppArmadillo.h>

// [[Rcpp::depends(RcppArmadillo)]]

using namespace arma; 
using namespace Rcpp;

// [[Rcpp::export]]
mat mvrnormArma(int n, mat sigma) {
   int ncols = sigma.n_cols;
   mat Y = randn(n, ncols);
   return Y * chol(sigma);
}
 

Предположим, что цель состоит в том, чтобы сгенерировать 10 000 10-мерных многомерных нормальных переменных, используя следующие две функции:

 PureRcpp = function(n){mvrnormArma(n, diag(10))}
LoopRcpp = function(n){for(ii in 1:n){mvrnormArma(1, diag(10))}}
 

Здесь, PureRcpp конечно, предпочтительнее и намного быстрее решение. Однако в некоторых приложениях может потребоваться полагаться на отдельные оценки mvrnormArma в цикле R. Это подход, LoopRcpp который, безусловно, является более медленным решением. Однако я был немного удивлен, когда провел сравнительный анализ и увидел, НАСКОЛЬКО медленным на самом деле было второе решение:

 > microbenchmark::microbenchmark(PureRcpp(10000), LoopRcpp(10000))
Unit: milliseconds
            expr       min        lq      mean    median        uq      max neval cld
 PureRcpp(10000)  2.236624  2.365988  2.578869  2.435268  2.565488 10.79609   100  a 
 LoopRcpp(10000) 52.590143 53.315655 58.080897 55.406020 62.264711 80.96275   100   b
 

Является ли это массовое замедление просто чем-то, с чем нам приходится жить, когда нам приходится работать в циклах R, или есть какие-то возможности уменьшить накладные расходы, возникающие из-за зацикливания? Я знаю, что мы могли бы просто переписать все на C , но цель состоит в том, чтобы предложить быстрое решение «Rcpp в цикле R», если это возможно.

Комментарии:

1. Здесь вам нужно понять только одну вещь: циклы в R не являются медленными. Вызовы функций выполняются медленно, в частности, если эти функции являются замыканиями (т. Е. is.primitive возвратами FALSE для них). Теперь сравните количество вызовов функций R для двух подходов.

2. Замечание принято, спасибо!

Ответ №1:

Как отметил Роланд, это в основном связано с вызовами функций. Однако вы можете сэкономить некоторое время (и получить более точное сравнение), оптимизировав / адаптировав свой код.

  • Переход к функции Cpp по ссылке
  • Не создавайте диагональ в цикле
  • Используйте вектор в одной отправке
  • Рисование векторизованных случайных чисел
 // [[Rcpp::export]]
mat draw_randn(int n, int ncols) {
  mat Y = randn(n, ncols);
  return(Y);
}
// [[Rcpp::export]]
mat mvrnormArma(mat sigma, mat Y) {
  return Y * chol(sigma);
}
// [[Rcpp::export]]
mat mvrnormArma_loop(matamp; sigma, rowvecamp; Y) {
  return Y * chol(sigma);
}
 

И протестируйте это.

 PureRcpp = function(n) {
  Y <- draw_randn(n, 10)
  I <- diag(10)
  mvrnormArma(I, Y)
}
LoopRcpp = function(n) {
  Y <- draw_randn(n, 10)
  I <- diag(10)
  for(ii in 1:n) {mvrnormArma_loop(I, Y[ii, ])}
}
 

Для меня это занимает около 10 мс.