MPI 和提升 multiprecision/gmp
MPI and boost multiprecision/gmp
我厌倦了将 DBL 类型数组转换为 char 数组,使用 MPI_Bcast 广播它并转换回 DBL 数组。 DBL 数组可以是以下任何一个:
- 双倍
- 长双
- 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 main
给出正确答案:
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 -- 打开 MPI C++ 包装器编译器、libboost-all-dev、libboost-tools-dev
有什么想法吗?
正如评论所说,你只能按位序列化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 的支持,而 Boost Serialization 又具有 built-in support for multiprecision.
所以我建议使用它,让自己免于头痛。
简而言之,你已经不在C区了。如果您使用的是 C++,则无法做出 C 程序员倾向于做出的假设。这是最好的,因为您也不需要再编写未定义的行为,或手动完成所有繁琐的工作:)
提升 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;
}
}
我厌倦了将 DBL 类型数组转换为 char 数组,使用 MPI_Bcast 广播它并转换回 DBL 数组。 DBL 数组可以是以下任何一个:
- 双倍
- 长双
- 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 main
给出正确答案:
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 -- 打开 MPI C++ 包装器编译器、libboost-all-dev、libboost-tools-dev
有什么想法吗?
正如评论所说,你只能按位序列化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 的支持,而 Boost Serialization 又具有 built-in support for multiprecision.
所以我建议使用它,让自己免于头痛。
简而言之,你已经不在C区了。如果您使用的是 C++,则无法做出 C 程序员倾向于做出的假设。这是最好的,因为您也不需要再编写未定义的行为,或手动完成所有繁琐的工作:)
提升 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;
}
}