tstollenw
tstollenw

Reputation: 193

Sending complex data with skeleton/content mechanism in Boost MPI

It seems that sending stl complex data via the skeleton/content mechanism does not work.

Here is a simple code that shows the problem:

#include <boost/mpi.hpp>
#include <boost/serialization/complex.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
namespace mpi = boost::mpi;
using namespace std;

int main(int argc, char* argv[])
{
     mpi::environment env(argc, argv);
     mpi::communicator world;
     int NN=world.size();
     int myid=world.rank();

     if (myid == 0)
     {
         int N = 10;
         vector <complex<double> > l(N);
         for (int p=1; p!=NN; p++)
             {
             int taskid=1;
             world.send(p, 0, taskid);
             world.send(p, 1, mpi::skeleton(l));
         }
         mpi::content c = mpi::get_content(l);
             for (int n = 0; n!=l.size() ; n++)
         {
             l[n]=complex<double>(1.0,1.0);
         }
         for (int p=1; p!=NN; p++)
         {
                 world.send(p, 1, c);
         }
     }
     else if (myid == 2)
     {
         vector <complex<double> > l;
         mpi::content c;
         world.recv(0, 1, mpi::skeleton(l));
         c = mpi::get_content(l);
         world.recv(0, 1, c);
         for (int n=0; n!=l.size(); n++)
         {
                 cout << l[n] << " ";
         }
         cout << endl;
     }
} 

The entries of the vector l on the output are not (1.0,1.0) but they seem to be uninitialized. This only occurs if one uses complex data types AND the skeleton/content mechanism.

Does anyone know, if this is a build in problem, or if I am doing something wrong?

Upvotes: 3

Views: 955

Answers (3)

Sosi
Sosi

Reputation: 1

namespace boost { 
namespace mpi {
    template <>
    struct is_mpi_datatype< complex<double> > : mpl::true_ { };
} }

using this can solve the problem. In Boost Serialization Optimization

Upvotes: 0

tstollenw
tstollenw

Reputation: 193

Here is a possible workaround for the problem:

I wrote my own serialization for the complex types:

#include <boost/mpi.hpp>
#include <boost/serialization/vector.hpp>
#include <iostream>
namespace mpi = boost::mpi;
using namespace std;

namespace boost
{
    namespace serialization
    {
        template<class Archive>
        void serialize(Archive & ar, complex<double> & g, const unsigned int version)
        {
            ar & g.real();
            ar & g.imag();
        }
    }
}

int main(int argc, char* argv[])
{
    mpi::environment env(argc, argv);
    mpi::communicator world;
    int NN=world.size();
    int myid=world.rank();

    if (myid == 0) 
    {
        int N = 10;
        vector <complex<double> > l(N);
        for (int p=1; p!=NN; p++)
        {
            world.send(p, 1, mpi::skeleton(l));
        }
        mpi::content c = mpi::get_content(l);
        for (int n = 0; n!=l.size() ; n++) 
        {
            l[n]=complex<double>(1.0,1.0);
        }
        for (int p=1; p!=NN; p++)
        {
            world.send(p, 1, c);
        }
    } 
    else 
    {
        vector <complex<double> > l;
        mpi::content c;
        world.recv(0, 1, mpi::skeleton(l));
        c = mpi::get_content(l);
        world.recv(0, 1, c);
        for (int n=0; n!=l.size(); n++)
        {
            cout << l[n] << " ";
        }
        cout << endl;
    }
}

Upvotes: 0

Lutorm
Lutorm

Reputation: 379

I ran into a similar problem with one of my classes recently, and after some stepping through the serialization procedure in the debugger, I think I figured out what's going on. The problem is using temporaries in the serialization operators. In boost/serialization/complex.hpp, the serialization functions look like this:

template<class Archive, class T>
inline void save(
    Archive & ar,
    std::complex< T > const & t,
    const unsigned int /* file_version */
){
    const T re = t.real();
    const T im = t.imag();
    ar << boost::serialization::make_nvp("real", re);
    ar << boost::serialization::make_nvp("imag", im);
}

template<class Archive, class T>
inline void load(
    Archive & ar,
    std::complex< T >& t,
    const unsigned int /* file_version */ 
){
    T re;
    T im;
    ar >> boost::serialization::make_nvp("real", re);
    ar >> boost::serialization::make_nvp("imag", im);
    t = std::complex< T >(re,im);
}

Notice that the serialization is done through temporaries. The get_content functionality works by building an MPI datatype, which really is a map of the locations of content in memory. When receiving the message, MPI copies the data directly into those locations, without calling any serialization operators. The problem is that when "save" uses temporaries, get_content() won't get the locations of the actual data but the temporaries, so the data received is not put into the right place.

For the skeleton/content functionality to work, my impression is that the save function (which is what get_content() calls) must directly, and only, serialize the data members of the object. In this case it can't, because it doesn't have access to the internal representation of complex. One would have to write the serialization operators as members of the class, I think.

(These limitations appear to not be mentioned at all in the Boost::MPI documentation.)

Upvotes: 3

Related Questions