HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Inner product of random floats written to file

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
randomfilewrittenproductinnerfloats

Problem

My goals are to make this code:

  • faster



  • more idiomatic C++



What the code does:

  • take an integer command-line argument N



  • make a vector of N random floating point values



  • make a matrix of N*N random floating point values



  • do inner product of vector * matrix



  • write the resulting vector to a file



Currently compiled with: g++ myFile.cpp -std=c++11 -O3

#include 
#include 

const std::string outputName = "output/coutput.txt"; // the name of the file this program makes

std::ostream& operator& v)
{ // pretty-print a vector of doubles using operator(RAND_MAX);
}

int main(int argc, char* argv[])
{
  // file i/o
  std::ofstream result;
  result.open(outputName, std::ios::out);

  srand(1);
  long elems = std::atol(argv[1]); // command line param
  std::vector m(elems * elems); // (elems * elems); // actually this is a vector of length (N*N)
  std::vector a(elems);
  std::vector r(elems, 0); // to store the result

  for (auto i = 0; i < elems; i++) // generate random vector
    a[i] = genRand();

  for (auto i = 0; i < elems * elems; i++) // generate random matrix
    m[i] = genRand();

  for (auto i = 0; i < m.size(); i++) { 
    auto idx = i / a.size();
    r[idx] += a[idx] * m[i];
  }

  result << r << std::endl; // print results
  result.close();
}

Solution

If you're going to use C++11, you should firstly look at throwing out rand, and using the newer features found in `. The setup is a bit more complex, but the distributions are generally much better statistically. Also, instead of generating numbers one at a time, we're going to generate them in a batch:

template >
Cont generate_rand(std::size_t n)
{
    Cont results;
    results.reserve(n);

    std::random_device rd;
    std::mt19937 generator(rd());
    std::uniform_real_distribution<> dist;
    auto random = [&]() { return dist(generator); };
    std::generate_n(std::back_inserter(results), n, random);
    return results;
}


With this, we can generate a set of random values into a vector:

// Read into num however many values we want to generate from args
auto vec = generate_rand(num);


Working with an actual matrix is a bit easier, so let's generate that (instead of an
n * n length vector):

std::vector> matrix;
for(auto i = 0; i < num; ++i) {
    matrix.emplace_back(generate_rand(num));
}


Something to hold our results, with the size correctly reserved:

std::vector result;
result.reserve(num);


The algorithm library already has a function that will do what you want:
std::inner_product:

for(auto it = matrix.begin(); it != matrix.end(); ++it) {
    auto v = std::inner_product(std::begin(vec), std::end(vec), std::begin(*it), 0.0);
    result.push_back(v);
}


Alternately, this is the place where a library like Eigen comes in really handy; with the correct includes, this can be written as follows:

#include 

typedef Eigen::MatrixXd Dynamic2d;

int main()
{
    // Initialize num from program args
    auto x = Dynamic2d::Random(num, num);
    auto y = Dynamic2d::Random(1, num);
    auto res = y * x;
}


(Note however that
Random gives a range between [-1, 1] when called like this, so you'd need to potentially do a bit of work to get that distribution back to [0, 1]`). This is likely to give you the maximum performance, as libraries like Eigen have had a lot of time spent on making them highly efficient.

Code Snippets

template <typename Cont = std::vector<double>>
Cont generate_rand(std::size_t n)
{
    Cont results;
    results.reserve(n);

    std::random_device rd;
    std::mt19937 generator(rd());
    std::uniform_real_distribution<> dist;
    auto random = [&]() { return dist(generator); };
    std::generate_n(std::back_inserter(results), n, random);
    return results;
}
// Read into num however many values we want to generate from args
auto vec = generate_rand(num);
std::vector<std::vector<double>> matrix;
for(auto i = 0; i < num; ++i) {
    matrix.emplace_back(generate_rand(num));
}
std::vector<double> result;
result.reserve(num);
for(auto it = matrix.begin(); it != matrix.end(); ++it) {
    auto v = std::inner_product(std::begin(vec), std::end(vec), std::begin(*it), 0.0);
    result.push_back(v);
}

Context

StackExchange Code Review Q#57052, answer score: 7

Revisions (0)

No revisions yet.