获得非负性成分的最快方法

时间:2016-08-17 06:45:44

标签: r performance rcpp

获得双向量的非负分量的更快方法是什么?也就是说,

{
    "my_bean_name": "Sth"
    //...
}

我的尝试是使用Rcpp:

pmax(x, 0)

这是一个适度的改进:

//' @title Parallel maximum
//' @description A faster \code{pmax()}.
//'
//' @name pmaxC
//' @param x A numeric vector.
//' @param a A single numeric value.
//' @return The parallel maximum of the input values.
//' @note This function will always be faster than \code{pmax(x, a)} when \code{a} is a single value, but can be slower than \code{pmax.int(x, a)} when \code{x} is short. Use this function when comparing a numeric vector with a single value.
//' @export pmaxC

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
NumericVector pmaxC(NumericVector x, double a) {
  int n = x.length();
  NumericVector out(n);

  for (int i = 0; i < n; ++i) {
    double xi = x[i];
    if (xi < a) {
      out[i] = a;
    } else {
      out[i] = xi;
    }
  }

  return out;
}

两者都没有令人无法接受的缓慢,但鉴于这是一种常见的情况,我想知道一个软件包是否已经开发出更快的方法。

1 个答案:

答案 0 :(得分:2)

您正在执行的操作非常简单,因此我不确定您的算法在上面有很大的改进空间。但是,如果真的需要挤出额外的性能,这似乎是并行化的一个很好的候选者。以下是使用RcppParallel

的可能实现
// [[Rcpp::depends(RcppParallel)]]
#include <RcppParallel.h>
#include <Rcpp.h>

struct Pmax : public RcppParallel::Worker {
    struct Apply {
        double mx;
        Apply(double mx_)
            : mx(mx_)
        {}

        double operator()(const double x) const 
        {
            return x > mx ? x : mx;
        }
    };

    const RcppParallel::RVector<double> input;
    RcppParallel::RVector<double> output;

    Apply f; 

    Pmax(const Rcpp::NumericVector input_,
         Rcpp::NumericVector output_,
         double mx_) 
    : input(input_), output(output_), f(mx_)
    {}

    void operator()(std::size_t begin, std::size_t end)
    {
        std::transform(
            input.begin() + begin,
            input.begin() + end,
            output.begin() + begin,
            f
        );
    }
};

// [[Rcpp::export]]
Rcpp::NumericVector par_pmax(Rcpp::NumericVector x, double y)
{
    Rcpp::NumericVector res = Rcpp::no_init_vector(x.size());
    Pmax p(x, res, y);
    RcppParallel::parallelFor(0, x.size(), p);

    return res;
} 

使用您的示例数据对此进行测试,我得到了合理的改进:

set.seed(5)
x <- rnorm(1e6)

all.equal(pmax(x, 0), par_pmax(x, 0))
#[1] TRUE  

microbenchmark::microbenchmark(
    pmax(x, 0), 
    pmaxC(x, 0),
    par_pmax(x, 0),
    times = 500L
)
# Unit: milliseconds
#            expr       min        lq      mean    median        uq       max neval
#      pmax(x, 0) 11.843528 12.193126 14.972588 13.030448 16.799250 102.09895   500
#     pmaxC(x, 0)  7.804883  8.036879 10.462070  8.772635 12.407587  69.08290   500
#  par_pmax(x, 0)  2.244691  2.443971  4.552169  2.624008  6.359027  65.99233   500