1+ // Investigate a couple methods for computing the variance.
2+
3+ // [[Rcpp::depends(RcppParallel)]]
4+ #define RCPP_PARALLEL_USE_SIMD
5+ #include < RcppParallel.h>
6+ using namespace RcppParallel ;
7+
8+ #include < Rcpp.h>
9+ using namespace Rcpp ;
10+
11+ class SumOfSquaresReducer
12+ {
13+ public:
14+
15+ explicit SumOfSquaresReducer (double mean)
16+ : mean_(mean)
17+ {}
18+
19+ template <typename T>
20+ void map (const T& self, T* pBuffer)
21+ {
22+ *pBuffer += boost::simd::sqr (self - mean_);
23+ }
24+
25+ template <typename T, typename U>
26+ void reduce (const T& data, U* pBuffer)
27+ {
28+ *pBuffer += boost::simd::sum (data);
29+ }
30+
31+ private:
32+ double mean_;
33+ };
34+
35+ // [[Rcpp::export]]
36+ double simdVarTwoPass (NumericVector x)
37+ {
38+ double total = simdReduce (x.begin (), x.end (), 0.0 , simd_ops::plus ());
39+ double n = x.size ();
40+ double mean = total / n;
41+
42+ double ssq = simdMapReduce (x.begin (), x.end (), 0.0 , SumOfSquaresReducer (mean));
43+
44+ return ssq / (n - 1 );
45+ }
46+
47+ // A one-pass method for computation of the variance.
48+ // Numerically unstable, relative to other methods.
49+ class SumOfSquares
50+ {
51+ public:
52+
53+ SumOfSquares (double shift, std::size_t n)
54+ : lhs_(0.0 ), pLhs_(0.0 ),
55+ rhs_ (0.0 ), pRhs_(0.0 ),
56+ shift_(shift), n_(n)
57+ {}
58+
59+ void operator ()(double data) {
60+ lhs_ += (data - shift_) * (data - shift_);
61+ rhs_ += (data - shift_);
62+ }
63+
64+ void operator ()(const boost::simd::pack<double >& data) {
65+ pLhs_ += (data - shift_) * (data - shift_);
66+ pRhs_ += (data - shift_);
67+ }
68+
69+ operator double () const {
70+ double lhs = lhs_ + boost::simd::sum (pLhs_);
71+ double rhs = rhs_ + boost::simd::sum (pRhs_);
72+ return lhs - (rhs * rhs) / n_;
73+ }
74+
75+ private:
76+ double lhs_;
77+ boost::simd::pack<double > pLhs_;
78+
79+ double rhs_;
80+ boost::simd::pack<double > pRhs_;
81+
82+ double shift_;
83+ std::size_t n_;
84+ };
85+
86+ // [[Rcpp::export]]
87+ double simdVarOnePass (NumericVector x, double shift = 0.0 )
88+ {
89+ std::size_t n = x.size ();
90+ double ssq = simdFor (x.begin (), x.end (), SumOfSquares (shift, n));
91+ return ssq / (n - 1 );
92+ }
93+
94+ /* ** R
95+ x <- rnorm(1E7, mean = 1E6)
96+ simdVarTwoPass(x)
97+ simdVarOnePass(x) # ouch!
98+ simdVarOnePass(x, 1E6)
99+ microbenchmark(
100+ simdVarOnePass(x),
101+ simdVarOnePass(x, 1E6),
102+ simdVarTwoPass(x)
103+ )
104+ */
0 commit comments