Basically, I want to do sort(sample(n, replace = TRUE))
, for n = 1e6
, and many times (e.g. 1e4 times).
Any way to do it faster in R(cpp)?
Implementing @Frank's idea in Rcpp:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
IntegerVector sort_sample(int n) {
IntegerVector tab(n);
for (int i = 0; i < n; i++) {
int k = n * unif_rand();
tab[k]++;
}
return tab;
}
Benchmark:
microbenchmark::microbenchmark(
sort(sample(n, replace = TRUE)),
tabulate(sample(n, replace = TRUE), n),
sort_sample(n)
)
For n = 1e5:
Unit: microseconds
expr min lq mean median uq max neval
sort(sample(n, replace = TRUE)) 3214.726 3393.606 3667.1001 3507.0525 3716.3560 7007.411 100
tabulate(sample(n, replace = TRUE), n) 1087.543 1104.215 1245.0722 1136.9085 1218.5865 4265.075 100
sort_sample(n) 789.403 812.780 870.2723 837.3445 924.4395 1188.432 100
For n = 1e6:
Unit: milliseconds
expr min lq mean median uq max neval
sort(sample(n, replace = TRUE)) 49.96651 52.58784 61.19775 55.09312 58.43035 160.16275 100
tabulate(sample(n, replace = TRUE), n) 13.74391 14.44253 17.22742 15.99816 17.54367 48.72374 100
sort_sample(n) 12.80741 14.40371 17.98320 15.31699 17.53548 63.21692 100
As mentioned by @Frank in the comments, it makes sense to use tabulate
instead of sort
. Once one uses that, it makes sense to think about faster sampling methods as provided by my dqrng package:
library(dqrng)
m <- 1e6
bm <- bench::mark(sort(sample.int(m, replace = TRUE)),
tabulate(sample.int(m, replace = TRUE)),
sort(dqsample.int(m, replace = TRUE)),
tabulate(dqsample.int(m, replace = TRUE)),
check = FALSE)
bm[, 1:4]
#> # A tibble: 4 x 4
#> expression min median `itr/sec`
#> <bch:expr> <bch:tm> <bch:tm> <dbl>
#> 1 sort(sample.int(m, replace = TRUE)) 72.3ms 75.5ms 13.2
#> 2 tabulate(sample.int(m, replace = TRUE)) 22.8ms 27.7ms 34.6
#> 3 sort(dqsample.int(m, replace = TRUE)) 59.5ms 64ms 15.3
#> 4 tabulate(dqsample.int(m, replace = TRUE)) 14.4ms 16.3ms 57.0
Created on 2019-06-27 by the reprex package (v0.3.0)
Note that I am still using R 3.5 on this machine. With R 3.6, the difference between sample.int
and dqsample.int
would be larger. Note also that one no longer needs a development version of dqrng to get the fast sampling methods.
One can also use the RNGs from dqrng via C++, but that does not make much difference compared with tabulate(dqsample.int(...))
in my experience:
#include <Rcpp.h>
// [[Rcpp::depends(dqrng, sitmo)]]
#include <dqrng_generator.h>
#include <convert_seed.h>
#include <R_randgen.h>
// [[Rcpp::plugins(cpp11)]]
// [[Rcpp::export]]
Rcpp::IntegerVector sort_sample(uint32_t n) {
Rcpp::IntegerVector seed(2, dqrng::R_random_int);
auto rng = dqrng::generator(dqrng::convert_seed<uint64_t>(seed));
Rcpp::IntegerVector tab(n);
for (uint32_t i = 0; i < n; i++) {
uint32_t k = (*rng)(n);
tab[k]++;
}
return tab;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With