Отправка сложных данных с помощью механизма скелета / содержимого в Boost MPI

#boost #stl #mpi #complex-numbers

#boost #stl #mpi #комплексные числа

Вопрос:

Похоже, что отправка сложных данных stl через механизм скелета / содержимого не работает.

Вот простой код, который показывает проблему:

 #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;
     }
} 
  

Записи вектора l на выходе не являются (1.0, 1.0), но они кажутся
неинициализированный. Это происходит только в том случае, если используются сложные типы данных И
механизм скелета / содержимого.

Кто-нибудь знает, является ли это проблемой сборки, или я делаю что-то неправильно?

Ответ №1:

Недавно я столкнулся с аналогичной проблемой с одним из моих классов, и после некоторого пошагового выполнения процедуры сериализации в отладчике, я думаю, я понял, что происходит. Проблема заключается в использовании временных значений в операторах сериализации. В boost/serialization/complex.hpp функции сериализации выглядят следующим образом:

 template<class Archive, class T>
inline void save(
    Archive amp; ar,
    std::complex< T > const amp; 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 amp; ar,
    std::complex< T >amp; 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);
}
  

Обратите внимание, что сериализация выполняется с помощью временных файлов. Функциональность get_content работает путем создания типа данных MPI, который на самом деле является картой расположения содержимого в памяти. При получении сообщения MPI копирует данные непосредственно в эти местоположения, без вызова каких-либо операторов сериализации. Проблема в том, что когда «save» использует временные данные, get_content() получит расположение не фактических данных, а временных, поэтому полученные данные помещаются не в нужное место.

У меня сложилось впечатление, что для работы функциональности скелета / содержимого функция сохранения (которая вызывает get_content()) должна напрямую и только сериализовать элементы данных объекта. В этом случае это невозможно, потому что у него нет доступа к внутреннему представлению complex . Я думаю, нужно было бы написать операторы сериализации как члены класса.

(Эти ограничения, похоже, вообще не упоминаются в документации Boost::MPI.)

Комментарии:

1. Обратите внимание, что это, похоже, влияет и на другие типы. Например, сериализация перечисления также использует временное значение, так что это также не работает со skeleta.

2. Спасибо за ответ. По крайней мере, теперь я знаю, что это невозможно сделать с помощью сборки в boost/ serialization/complex.hpp.

3. @tstollenw: (Очевидно, я не могу добавить комментарии к вашему ответу, поэтому вместо этого я комментирую здесь.) Я думаю, что ваш обходной путь не гарантированно сработает, потому что, по крайней мере, в некоторых спецификациях, похоже, говорится, что real() и imag() возвращают значение.

Ответ №2:

Вот возможное решение проблемы:

Я написал свою собственную сериализацию для сложных типов:

 #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 amp; ar, complex<double> amp; g, const unsigned int version)
        {
            ar amp; g.real();
            ar amp; 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;
    }
}
  

Ответ №3:

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

использование этого может решить проблему. В оптимизации сериализации Boost