Skip to content

Commit fc2133a

Browse files
committed
compare algorithms for computing variance
1 parent 0ee1a8e commit fc2133a

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

examples/boost-simd-variance.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
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

Comments
 (0)