Why does Eigen matrix calculations return zero when std::thread is used for multi threading?

Viewed 60

I am trying to implement multi threading to distribute certain cpu intensive operations to different threads. However any Eigen declarations always returns zero when multi threading is used. Following is an example code that demonstrates this issue

#include "../Eigen/Core"
#include "../Eigen/LU"
#include "../Eigen/Sparse"
#include "../Eigen/StdVector"
#include <iostream>

#include <thread>
#include <mutex>

using namespace std;
using namespace Eigen;


void Multiply_3x3 (MatrixXd& matrix1, MatrixXd& matrix2, MatrixXd& results);

int main()
{

  MatrixXd a(3, 3);
  MatrixXd b(3, 3);
  MatrixXd c(3,3);

  a<<2,4,3,1,5,7,0,2,3;
  b<<2,4,3,1,5,7,0,2,3;
  
  //Multiply_3x3(a,b,c);
  std::thread thread_multi(Multiply_3x3, a, b, c);
  thread_multi.join();

  cout << " *** In Main Program ***" << endl;
  cout <<c(0,0)<<" , "<< c(0,1)<<" , "<< c(0,1)<<endl;
  cout<<c(1,0)<<" , "<<c(1,1)<<" , "<<c(1,1)<<endl;
  cout<<c(2,0)<<" , "<<c(2,1)<<" , "<<c(2,1)<<endl;


}

void Multiply_3x3 (MatrixXd& matrix1, MatrixXd& matrix2, MatrixXd& results)
{
  results = matrix1 * matrix2;

  cout << " *** In Multiply_3x3 ***" << endl;
  cout<<results(0,0)<<" , "<<results(0,1)<<" , "<<results(0,1)<<endl;
  cout<<results(1,0)<<" , "<<results(1,1)<<" , "<<results(1,1)<<endl;
  cout<<results(2,0)<<" , "<<results(2,1)<<" , "<<results(2,1)<<endl<<endl<<endl;  
}

The output from this run is

 *** In Multiply_3x3 ***
8 , 34 , 34
7 , 43 , 43
2 , 16 , 16


 *** In Main Program ***
1.69122e-306 , 1.95819e-306 , 1.95819e-306
9.34612e-307 , 6.23037e-307 , 6.23037e-307
9.34602e-307 , 6.2304e-307 , 6.2304e-307

As shown in the output the results are correct in the function Multiply_3x3. However the returned values are zero. If I don't use std:thread and call the function as shown in the commended section (//Multiply_3x3(a,b,c);) the results are correct in the calling function. Wondering if anyone could point out what I am missing here.

2 Answers

You have to apply std::ref to all matrices a, b and c when constructing thread. Without this, std::thread makes a copy passed arguments and operates on them, changing the copy of c matrix instead original c matrix defined in main.

  std::thread thread_multi(Multiply_3x3, std::ref(a), std::ref(b), std::ref(c));
  thread_multi.join();

Demo


Note, without std::ref your code doesn't compile on G++, it seems you are using Microsoft Visual which has some extensions (enabled by default) - for example, binding temporary object to lvalue reference. That is why your code was compiled. You can turn off this feature by adding /Za to compiler's flags.

Not entirely sure how you got this to compile and work. I'm not able to compile this on MSVC.

But, remember that the std::thread's constructor doesn't care about the type of the arguments passed in. It simply copies the arguments to an internal storage (accessible by the newly created thread) and are passed in as temporaries (rvalues) to the callable object.

For example,

void foo(std::string& s);
std::thread t1(foo, "hello");
std::string arg("hello");
std::thread t2(foo, arg);

will not work because it's not possible to construct a non-const reference from a temporary.

But

void foo(const std::string& s);
std::thread t1(foo, "hello");
std::string arg("hello");
std::thread t2(foo, arg);

is okay because the function takes in a const reference.

So you just have to wrap the arguments that a need a reference with std::ref, so that the function is not given a temporary copy. It will then compile and work properly.

Related