【问题标题】:MPI and boost multiprecision/gmpMPI 和提升多精度/gmp
【发布时间】:2019-12-18 00:56:48
【问题描述】:

我厌倦了将 DBL 类型数组转换为 char 数组,使用 MPI_Bcast 广播它并转换回 DBL 数组。 DBL 数组可以是以下任何一种:

  1. 长双
  2. mpf_float_50(在 boost/multiprecision/ 包中定义的类型)。

前两种工作正常:

mpicxx -D_DBL main.cpp -o main && mpirun -np 2 main

mpicxx -D_LDBL main.cpp -o main && mpirun -np 2 main

给予

before for rank=0
0
0.1
after for rank=1
0
0.1
after for rank=0
0
0.1

但对于

mpicxx -D_EDBL50 main.cpp -o main -lgmp && mpirun -np 2 main
one has
before for rank=0
0
0.1
after for rank=0
0
0.1
after for rank=1
0

[Fominskoe:06235] *** Process received signal ***
[Fominskoe:06235] Signal: Segmentation fault (11)
[Fominskoe:06235] Signal code: Address not mapped (1)
[Fominskoe:06235] Failing at address: 0x55892d02d450
[Fominskoe:06235] [ 0] /lib/x86_64-linux-gnu/libc.so.6(+0x3ef20)
[0x7f26e6333f20]
[Fominskoe:06235] [ 1] /usr/lib/x86_64-linux-
gnu/libgmp.so.10(__gmpn_copyi+0x4d)[0x7f26e71f5213]
[Fominskoe:06235]

* 错误信息结束 *


mpirun 注意到节点 Fominskoe 上 PID 为 0 的进程等级 1 在信号 11 上退出(分段错误)。

我注意到最后一个选择 mpirun -np 1 主要

给出正确答案:

before for rank=0
0
0.1
after for rank=0
0
0.1
    #include "mpi.h"

    #if defined(_DBL)
    typedef double DBL;
    #endif    

    #if defined(_LDBL)
    typedef long double DBL;
    #endif 

    #if defined(_EDBL50)
    #include <boost/multiprecision/gmp.hpp>
    using namespace boost::multiprecision;
    typedef mpf_float_50 DBL;
    #endif 

    using namespace std;

    int main(int argc, char* argv[])
    {

    MPI::Init (argc, argv);
    int proc_num = MPI::COMM_WORLD.Get_size ( );
    int my_rank  = MPI::COMM_WORLD.Get_rank ( );

    int N=2;

    DBL  DB[N];
    int  CN=N*sizeof(DBL);
    char CH[CN];

    if ( !my_rank ){

        cout<<"before for rank="<<my_rank<<endl;
        for (int i=0; i<N; i++) {               // init array
            DB[i]=i*0.1;
            cout<<DB[i]<<endl;
        }

        char* ptr=(char*)(&DB[0]);

        for (int i=0; i<CN; i++) 
            CH[i]=*ptr++;

        for (int i=0; i<N; i++) // clean
            DB[i]=0;

    }

    MPI_Bcast (CH, CN, MPI_CHAR, 0, MPI_COMM_WORLD);

    int ii=0;
    DBL* V;

    cout<<"\nafter for rank="<<my_rank<<endl;
    for (int i=0; i<N; i++) {
        V=(DBL*)(&CH[ii]); 
        DB[i]=*V;
        cout<< DB[i]<<endl;
        ii+=sizeof(DBL);
        //        if (my_rank)
        //        break;
    }

    MPI::Finalize();

    return 0;
}

如果 MPI 被移除,则转换 DBL->char->DBL 适用于所有三种类型。

Ubuntu 18.04, gcc, mpicxx -- Open MPI C++ wrapper compiler, libboost-all-dev, libboost-tools-dev

有什么想法吗?

【问题讨论】:

  • 我确定问题在于mpf_float_50 是复杂类型,无法以您尝试的简单方式进行序列化。
  • Plus DB[i]=0; 正在对您刚刚按字节复制到 CH 的对象调用析构函数。
  • 也许搜索“序列化”?您的循环只是 memcpy,仅适用于普通类型。

标签: c++ boost mpi gmp


【解决方案1】:

正如 cmets 所说,您只能按位序列化 trivial types

    char *ptr = (char *)(&DB[0]);

    for (int i = 0; i < CN; i++)
        CH[i] = *ptr++;

这不是转换。这是一个实现按位复制的重新解释转换,即Undefined Behaviour 用于非平凡的多精度类型。

你很幸运,因为你想使用 MPI 和 Boost,你可以使用 Boost MPI,它内置了对 Boost Serialization 的支持,而后者又具有built-in support for multiprecision

所以我建议使用它来避免头痛。

简而言之,您不再处于 C 领域。如果您使用的是 C++,则无法做出 C 程序员倾向于做出的假设。这是最好的,因为您也不需要再编写未定义的行为,或者手动完成所有繁琐的工作:)

Boost MPI 演示:

#include <boost/mpi.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/multiprecision/gmp.hpp>
#include <iostream>
#include <random>

namespace bmp = boost::multiprecision;
namespace mpi = boost::mpi;

//using Database = std::vector<bmp::mpf_float_50>;
using Database = std::vector<bmp::cpp_bin_float_50>;
static std::mt19937 prng { std::random_device{}() };

int main() {
    mpi::environment env;
    mpi::communicator world;

    if (world.rank() == 0) {
        std::cout << "before for rank=" << world.rank() << std::endl;

        Database db;

        std::generate_n(
                back_inserter(db),
                prng()%15, 
                [i=0]() mutable { return i++*0.1; });

        world.send(1, 1, db);
    } else {
        Database db;
        world.recv(0, 1, db);

        std::cout << "Received " << db.size() << " numbers: ";
        for (auto& number : db) {
            std::cout << number << " ";
        }
        std::cout << std::endl;
    }
}

运行时:

具有不可序列化的类型

如果您坚持使用mpfr 类型,我认为您将不得不手动进行序列化。一种非常幼稚的方法是将所有元素转换为字符串:

#include <boost/mpi.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/multiprecision/gmp.hpp>
#include <boost/convert/lexical_cast.hpp>
#include <boost/convert.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range.hpp>
#include <iostream>
#include <random>

namespace bmp = boost::multiprecision;
namespace mpi = boost::mpi;

using Num = bmp::mpf_float_50;
using Database = std::vector<Num>;
using SerializationFormat = std::vector<std::string>;
static auto serialize   = boost::cnv::apply<std::string>(boost::cnv::lexical_cast());
static auto deserialize = boost::cnv::apply<Num>(boost::cnv::lexical_cast());

//using Database = std::vector<bmp::cpp_bin_float_50>;
static std::mt19937 prng { std::random_device{}() };

int main() {
    mpi::environment env;
    mpi::communicator world;

    if (world.rank() == 0) {
        std::cout << "before for rank=" << world.rank() << std::endl;

        Database db;

        std::generate_n(
                back_inserter(db),
                prng()%15, 
                [i=0]() mutable { return i++*0.1; });

        SerializationFormat db_str;
        boost::transform(db, back_inserter(db_str), serialize);
        world.send(1, 1, db_str);
    } else {
        SerializationFormat db_str;
        world.recv(0, 1, db_str);

        Database db;
        boost::transform(db_str, back_inserter(db), deserialize);

        std::cout << "Received " << db.size() << " numbers: ";
        for (auto& number : db) {
            std::cout << number << " ";
        }
        std::cout << std::endl;
    }
}

【讨论】:

  • 添加了一个 Boost MPI 演示,展示了对 cpp_bin_float_50 的内置支持。
  • 感谢cmets的解答!我计划在未使用 boost 的 C++ MPI 代码中使用多精度数据。正如我所见,Boost 需要其特定的 MPI 初始化。推荐什么?事实上,我在这里使用 boost 只是因为我不知道如何使用 MPI 发送 GMP 数据的任何其他选项。我认为任何多精度数据类型都比 C long double 类型更准确,后者在 x86_64 架构下给出 1.0+1e-20=1.0。
  • 如果您愿意,您可以使用 GMP/MPFR 提供的任何其他序列化技术(二进制或文本)而不使用 Boost MPI。我只是走完整的 C++ 路线。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-26
  • 2016-03-14
  • 1970-01-01
  • 1970-01-01
  • 2018-06-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多