Давайте подойдем к этому экспериментально. Сколько памяти выделяется R и сколько времени это занимает? Во-первых, давайте используем вашу функцию и запускаем ее с разными аргументами. Я обертываю это в bench::mark
, так как это дает мне измерения оперативной памяти и процессора:
> bench::mark(rmexp1(100, 10),
+ rmexp1(100, 100),
+ rmexp1(100, 1000),
+ rmexp1(100, 10000),
+ check = FALSE)
#> # A tibble: 4 x 13
#> expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>
#> 1 rmexp1(100, 10) 46.93µs 52.61µs 16307. 10.35KB 8.24 7918 4
#> 2 rmexp1(100, 100) 381.41µs 538.42µs 1786. 3.9MB 4.14 863 2
#> 3 rmexp1(100, 1000) 4.83ms 5.08ms 187. 1.53MB 8.68 86 4
#> 4 rmexp1(100, 10000) 59.85ms 63.19ms 15.5 15.27MB 5.17 6 2
#> # … with 5 more variables: total_time <bch:tm>, result <list>, memory <list>,
#> # time <list>, gc <list>
Неудивительно, что большая матрица занимает больше времени и требует больше памяти. Кроме того, выделенная память примерно в два раза больше памяти, необходимой для выходной матрицы. Так что да, мы выделяем больше памяти, чем необходимо здесь.
Это критично для производительности? Это зависит. В конце концов, вы создаете случайные переменные с экспоненциальным распределением, которое занимает конечное время. Кроме того, вы делаете неуказанные вычисления в do something with values
, которые могут занять еще больше времени. Давайте избавимся от создания случайных переменных, используя альтернативные функции, которые выделяют память только с инициализацией или без ее обнуления:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericMatrix rmzero(int n, int d) {
NumericMatrix out(n, d);
NumericVector values;
for (int k=0; k<n; k++) {
values = Rcpp::NumericVector(d);
// do something with values
out(k, _) = values;
}
return out;
}
// [[Rcpp::export]]
NumericMatrix rmnoinit(int n, int d) {
NumericMatrix out(n, d);
NumericVector values;
for (int k=0; k<n; k++) {
values = Rcpp::NumericVector(Rcpp::no_init(d));
// do something with values
out(k, _) = values;
}
return out;
}
С bench::mark
мы получаем:
> bench::mark(rmexp1(100, 1000),
+ rmzero(100, 1000),
+ rmnoinit(100, 1000),
+ check = FALSE)
#> # A tibble: 3 x 13
#> expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
#> <bch:expr> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <dbl>
#> 1 rmexp1(100, 1000) 4.83ms 5.05ms 190. 1.53MB 8.72 87 4
#> 2 rmzero(100, 1000) 509.74µs 562.24µs 1510. 1.53MB 60.4 525 21
#> 3 rmnoinit(100, 1000) 404.24µs 469.43µs 1785. 1.53MB 53.8 664 20
#> # … with 5 more variables: total_time <bch:tm>, result <list>, memory <list>,
#> # time <list>, gc <list>
Так примерно только 1/10 времени выполнения вашей функции связано с выделением памяти и другими накладными расходами. Остальное происходит от случайных переменных.
Если генерация случайных переменных является фактическим узким местом в вашем коде, вас может заинтересовать мой пакет dqrng :
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::depends(dqrng)]]
#include <dqrng.h>
// [[Rcpp::export]]
NumericMatrix rmdqexp1(int n, int d) {
NumericMatrix out(n, d);
NumericVector values;
for (int k=0; k<n; k++) {
values = dqrng::dqrexp(d);
// do something with values
out(k, _) = values;
}
return out;
}
С bench::mark
мы получаем:
> bench::mark(rmexp1(100, 1000),
+ rmdqexp1(100, 1000),
+ check = FALSE)
#> # A tibble: 2 x 13
#> expression min median `itr/sec` mem_alloc `gc/sec` n_itr n_gc
#> <bch:expr> <bch:> <bch:> <dbl> <bch:byt> <dbl> <int> <dbl>
#> 1 rmexp1(100, 1000) 3.69ms 5.03ms 201. 1.53MB 6.36 95 3
#> 2 rmdqexp1(100, 1000) 1.09ms 1.21ms 700. 1.65MB 22.6 310 10
#> # … with 5 more variables: total_time <bch:tm>, result <list>, memory <list>,
#> # time <list>, gc <list>
Можно сэкономить немного времени, используя более быстрый генератор случайных чисел.