As part of a bigger mcmc sampling algorithm, I'm repeatedly looping through my entire dataset. When the data grows large this becomes a pretty large bottleneck in my algorithm and I'm looking for ways to make it faster.
Some things I'm considering are:
- What are the most efficient datatypes? For example, for the variable
x
I usestd::vector< std::vector<bool> >
because data are binary; 0 or 1. However, this leads to many type conversions. Is there a better way? - Can I use iterators to loop over the data more efficiently?
All suggestions are welcome!
Edit: the performance critical function does the following:
The goal of the code is to simulate a dataset of size np by ni, while simultaneously evaluating a likelihood ratio. The simulated new data is binary, and created by rlogis(0.0, 1.0) <= val ? 1 : 0
. The subsequent evaluation of the ratio happens via acc += theta[i][d] * (x[i][j] -
xnew) * C[j][d];
. These two steps seem optimal from a performance perspective to me; my question is primarily about the surrounding structure.
Code:
#include <iostream>
#include <vector>
#include <chrono>
using namespace std; // <-- only here for the example; I don't normally do this
typedef unsigned int uint;
typedef std::vector<double> vec;
typedef std::vector<vec> mat;
// will be replaced by actual random number generator
double rlogis() {
return rand() % 5 - 2;
}
// improving performance of this function is my goal
double orig(const uint np, const uint ni, const uint nd,
const mat& theta, const mat& C,
const vec& delta, const vec& lambdaNew,
const int d, std::vector<std::vector<bool> >& x) {
double acc = 0.0;
bool xnew;
vec tmp(nd);
for (uint j = 0; j < ni; ++j) {
for (uint d2 = 0; d2 < nd; ++d2) {
tmp[d2] = lambdaNew[d2] * C[j][d2];
}
for (uint i = 0; i < np; ++i) {
double val = delta[j];
for (uint d2 = 0; d2 < nd; ++d2) {
val += tmp[d2] * theta[i][d2];
}
xnew = rlogis() <= val;
acc += theta[i][d] * (x[i][j] - xnew) * C[j][d];
}
}
return acc;
}
// wrapper function for timing
int main() {
// dimensions of the data
const uint np = 2E4; // this could be larger, easily 1E5
const uint ni = 200; // this stays constant
const uint nd = 3; // could be 4-5, but not 10
// objects
std::vector<double> delta(ni);
std::vector<double> lambdaNew(nd);
vector< std::vector<double> > theta(np, std::vector<double>(nd));
vector< std::vector<double> > C(ni, std::vector<double>(nd));
std::vector< std::vector<bool> > x(np, std::vector<bool>(ni));
// fill objects with values (exact values are irrelevant)
srand(1);
int d = rand() % nd;
for (uint i = 0; i < np; ++i) {
for (uint d = 0; d < nd; ++d) {
theta[i][d] = 2 * rand() - 1;
}
for (uint j = 0; j < ni; ++j) {
x[i][j] = bool(int(rand() + .5));
}
}
for (uint j = 0; j < ni; ++j) {
delta[j] = 2 * rand() - 1;
for (uint d = 0; d < nd; ++d) {
C[j][d] = 2 * rand() - 1;
}
}
for (uint d = 0; d < nd; ++d) {
lambdaNew[d] = 5 * rand() + 1;
}
// start timing
{
srand(2);
auto start = chrono::steady_clock::now();
auto ans = orig(np, ni, nd, theta, C, delta, lambdaNew, d, x);
auto time1 = chrono::steady_clock::now() - start;
cout << "Orig: " << ans << "\n"
<< "Time: " << chrono::duration <double, milli> (time1).count() << " ms\n" << endl;
}
return 0;
}
Can I use iterators to loop over the data more efficiently?
As a smart man once said: when in doubt, measure! \$\endgroup\$std::vector<bool>
is amemory efficient but slow implementation similar to a bitset. Therefore, if you want performance you should definitely not use it \$\endgroup\$