MPI – 按订单打印

我正在尝试在C中编写一个函数,每个处理器都会打印自己的数据。 这是我有的:

void print_mesh(int p,int myid,int** U0,int X,int Y){ int i,m,n; for(i=0;i<p;i++){ if(myid==i){ printf("myid=%d\n",myid); for(n=0;n<X;n++){ for(m=0;m<Y;m++){ printf("%d ",U0[n][m]); } printf("\n"); } } else MPI_Barrier(MPI_COMM_WORLD); } } 

它由于某种原因不起作用。 arrays全部混合打印。 你有什么见解,为什么这不起作用? 还有其他有用的想法吗? 如果可能,我不想在主进程中发送整个数组。 另外,我不想使用预编译function。

无法保证来自许多不同进程的消息在到达另一个进程时将以“正确”的顺序到达。 这基本上就是这里发生的事情。

即使您没有明确发送消息,当您在屏幕上打印某些内容时,也必须将其发送到本地系统( mpiexecmpirun )上的进程,然后可以将其打印到屏幕上。 MPI无法知道这些消息的正确顺序是什么,所以它只是在它们到达时打印它们。

如果您要求按特定顺序打印消息,则必须将它们全部发送到一个等级,以便按照您喜欢的顺序打印它们。 只要一个排名完成所有打印,所有消息都将被正确排序。

应该说你可能会找到答案,你可以在那里找到你可以在字符串末尾添加换行符或使用flush()来确保刷新缓冲区,但这并不能保证在由于上述原因,远程端。

所以,你可以这样做:

 MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0) { MPI_Send(&message, 1, MPI_INT, 1, 0, MPI_COMM_WORLD); printf("1 SIZE = %d RANK = %d MESSAGE = %d \n",size,rank, message); } else { int buffer; MPI_Status status; MPI_Probe(MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status); MPI_Get_count(&status, MPI_INT, &buffer); if (buffer == 1) { printf("2 SIZE = %d RANK = %d MESSAGE = %d \n",size,rank, message); MPI_Recv(&message, buffer, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &status); if (rank + 1 != size) { MPI_Send(&message, 1, MPI_INT, ++rank, 0, MPI_COMM_WORLD); } }; }; MPI_Finalize(); 

执行后:

 $ mpirun -n 5 ./a.out 1 SIZE = 5 RANK = 0 MESSAGE = 999 2 SIZE = 5 RANK = 1 MESSAGE = 999 2 SIZE = 5 RANK = 2 MESSAGE = 999 2 SIZE = 5 RANK = 3 MESSAGE = 999 2 SIZE = 5 RANK = 4 MESSAGE = 999 

我受到了СвятославПавленк®答案的启发:使用阻塞MPI通信来强制执行串行输出。 虽然韦斯利布兰德有一个关于MPI不是为串行输出而构建的观点。 因此,如果我们想要输出数据,那么每个处理器输出(非冲突)数据都是有意义的。 或者,如果数据的顺序很重要(并且它不是太大),推荐的方法是将其全部发送到cpu(比如排名0),然后正确地格式化数据。

对我来说,这似乎有点过分,特别是当数据可以是变长字符串时,这常常是std::cout << "a=" << some_varible << " b=" << some_other_variable经常是。 因此,如果我们想要一些快速和肮脏的有序打印,我们可以利用СвятославПавленк®的答案来构建一个串行输出流。 这个解决方案工作正常, 它的性能与许多cpu一样严重,所以不要使用它的数据输出!

 #include  #include  #include  

MPI管家:

 int mpi_size; int mpi_rank; void init_mpi(int argc, char * argv[]) { MPI_Init(& argc, & argv); MPI_Comm_size(MPI_COMM_WORLD, & mpi_size); MPI_Comm_rank(MPI_COMM_WORLD, & mpi_rank); } void finalize_mpi() { MPI_Finalize(); } 

支持MPI消息链接的通用类

 template class MPIChain{ // Uses a chained MPI message (T) to coordinate serial execution of code (the content of the message is irrelevant). private: T message_out; // The messages aren't really used here T message_in; int size; int rank; public: void next(){ // Send message to next core (if there is one) if(rank + 1 < size) { // MPI_Send - Performs a standard-mode blocking send. MPI_Send(& message_out, 1, MPI_T, rank + 1, 0, MPI_COMM_WORLD); } } void wait(int & msg_count) { // Waits for message to arrive. Message is well-formed if msg_count = 1 MPI_Status status; // MPI_Probe - Blocking test for a message. MPI_Probe(MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, & status); // MPI_Get_count - Gets the number of top level elements. MPI_Get_count(& status, MPI_T, & msg_count); if(msg_count == 1) { // MPI_Recv - Performs a standard-mode blocking receive. MPI_Recv(& message_in, msg_count, MPI_T, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, & status); } } MPIChain(T message_init, int c_rank, int c_size): message_out(message_init), size(c_size), rank(c_rank) {} int get_rank() const { return rank;} int get_size() const { return size;} }; 

我们现在可以使用我们的MPIChain类创建我们的类来管理输出流:

 class ChainStream : public MPIChain { // Uses the MPIChain class to implement a ostream with a serial operator<< implementation. private: std::ostream & s_out; public: ChainStream(std::ostream & os, int c_rank, int c_size) : MPIChain(0, c_rank, c_size), s_out(os) {}; ChainStream & operator<<(const std::string & os){ if(this->get_rank() == 0) { this->s_out << os; // Initiate chain of MPI messages this->next(); } else { int msg_count; // Wait untill a message arrives (MPIChain::wait uses a blocking test) this->wait(msg_count); if(msg_count == 1) { // If the message is well-formed (ie only one message is recieved): output string this->s_out << os; // Pass onto the next member of the chain (if there is one) this->next(); } } // Ensure that the chain is resolved before returning the stream MPI_Barrier(MPI_COMM_WORLD); // Don't output the ostream! That would break the serial-in-time exuction. return *this; }; }; 

注意operator<<末尾的MPI_Barrier 。 这是为了防止代码启动第二个输出链。 即使这可以移到operator<<之外,我想我会把它放在这里,因为这应该是串行输出....

把它们放在一起:

 int main(int argc, char * argv[]) { init_mpi(argc, argv); ChainStream cs(std::cout, mpi_rank, mpi_size); std::stringstream str_1, str_2, str_3; str_1 << "FIRST: " << "MPI_SIZE = " << mpi_size << " RANK = " << mpi_rank << std::endl; str_2 << "SECOND: " << "MPI_SIZE = " << mpi_size << " RANK = " << mpi_rank << std::endl; str_3 << "THIRD: " << "MPI_SIZE = " << mpi_size << " RANK = " << mpi_rank << std::endl; cs << str_1.str() << str_2.str() << str_3.str(); // Equivalent to: //cs << str_1.str(); //cs << str_2.str(); //cs << str_3.str(); finalize_mpi(); } 

请注意,在向ChainStream实例发送字符串之前,我们将字符串str_1str_2str_3连接在一起。 通常人们可以这样做:

 std::cout << "a" << "b" << "c"" << std::endl 

但这从左到右应用operator<< ,我们希望字符串顺序运行每个进程之前准备好输出。

 g++-7 -O3 -lmpi serial_io_obj.cpp -o serial_io_obj mpirun -n 10 ./serial_io_obj 

输出:

 FIRST: MPI_SIZE = 10 RANK = 0 FIRST: MPI_SIZE = 10 RANK = 1 FIRST: MPI_SIZE = 10 RANK = 2 FIRST: MPI_SIZE = 10 RANK = 3 FIRST: MPI_SIZE = 10 RANK = 4 FIRST: MPI_SIZE = 10 RANK = 5 FIRST: MPI_SIZE = 10 RANK = 6 FIRST: MPI_SIZE = 10 RANK = 7 FIRST: MPI_SIZE = 10 RANK = 8 FIRST: MPI_SIZE = 10 RANK = 9 SECOND: MPI_SIZE = 10 RANK = 0 SECOND: MPI_SIZE = 10 RANK = 1 SECOND: MPI_SIZE = 10 RANK = 2 SECOND: MPI_SIZE = 10 RANK = 3 SECOND: MPI_SIZE = 10 RANK = 4 SECOND: MPI_SIZE = 10 RANK = 5 SECOND: MPI_SIZE = 10 RANK = 6 SECOND: MPI_SIZE = 10 RANK = 7 SECOND: MPI_SIZE = 10 RANK = 8 SECOND: MPI_SIZE = 10 RANK = 9 THIRD: MPI_SIZE = 10 RANK = 0 THIRD: MPI_SIZE = 10 RANK = 1 THIRD: MPI_SIZE = 10 RANK = 2 THIRD: MPI_SIZE = 10 RANK = 3 THIRD: MPI_SIZE = 10 RANK = 4 THIRD: MPI_SIZE = 10 RANK = 5 THIRD: MPI_SIZE = 10 RANK = 6 THIRD: MPI_SIZE = 10 RANK = 7 THIRD: MPI_SIZE = 10 RANK = 8 THIRD: MPI_SIZE = 10 RANK = 9 

MPI标准没有规定应该如何收集来自不同节点的stdout,并且fflush没有帮助。

如果您需要按顺序打印大输出,可能最好的解决方案是不要将它们全部收集并立即打印,因为这将通过网络生成流量。 更好的解决方案是创建类似于虚拟环的东西,其中每个进程等待来自前一进程的令牌,打印并将令牌发送到下一个进程。 当然第一个过程不必等待,它会打印并发送到下一个过程。

无论如何,如果输出真的很大,可能没有意义在video上打印输出,你应该使用Jonathan Dursi建议的MPI-IO。