PaGMO  1.1.5
mpi_environment.cpp
1 /*****************************************************************************
2  * Copyright (C) 2004-2015 The PaGMO development team, *
3  * Advanced Concepts Team (ACT), European Space Agency (ESA) *
4  * *
5  * https://github.com/esa/pagmo *
6  * *
7  * act@esa.int *
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  * This program is distributed in the hope that it will be useful, *
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
17  * GNU General Public License for more details. *
18  * *
19  * You should have received a copy of the GNU General Public License *
20  * along with this program; if not, write to the *
21  * Free Software Foundation, Inc., *
22  * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
23  *****************************************************************************/
24 
25 #include <boost/shared_ptr.hpp>
26 #include <cstdlib>
27 #include <mpi.h>
28 #include <stdexcept>
29 #include <utility>
30 
31 #include "exceptions.h"
32 #include "algorithm/base.h"
33 #include "population.h"
34 #include "mpi_environment.h"
35 
36 namespace pagmo
37 {
38 
39 bool mpi_environment::m_initialised = false;
40 bool mpi_environment::m_multithread = false;
41 
43 
52 {
53  if (m_initialised) {
54  pagmo_throw(std::runtime_error,"cannot re-initialise the MPI environment");
55  }
56  m_initialised = true;
57  int thread_level_provided;
58  MPI_Init_thread(NULL,NULL,MPI_THREAD_MULTIPLE,&thread_level_provided);
59  if (thread_level_provided >= MPI_THREAD_MULTIPLE) {
60  m_multithread = true;
61  }
62  if (get_rank()) {
63  // If this is a slave, it will have to stop here, listen for jobs, execute them, and exit()
64  // when signalled to do so.
65  listen();
66  }
67  // If this is the root node, it will need to be able to call MPI from multiple threads.
68  if (thread_level_provided < MPI_THREAD_SERIALIZED && get_rank() == 0) {
69  pagmo_throw(std::runtime_error,"the master node must support at least the MPI_THREAD_SERIALIZED thread level");
70  }
71  // World sizes less than 2 are not allowed.
72  if (get_size() < 2) {
73  pagmo_throw(std::runtime_error,"the size of the MPI world must be at least 2");
74  }
75 }
76 
78 
82 {
83  // In theory this should never be called by the slaves.
84  pagmo_assert(!get_rank());
85  pagmo_assert(m_initialised);
86  std::pair<boost::shared_ptr<population>,algorithm::base_ptr> shutdown_payload;
87  for (int i = 1; i < get_size(); ++i) {
88  // Send the shutdown signal to all slaves.
89  send(shutdown_payload,i);
90  }
91  MPI_Finalize();
92  m_initialised = false;
93 }
94 
95 void mpi_environment::check_init()
96 {
97  if (!m_initialised) {
98  pagmo_throw(std::runtime_error,"MPI environment has not been initialised");
99  }
100 }
101 
103 
112 bool mpi_environment::iprobe(int source)
113 {
114  check_init();
115  MPI_Status status;
116  int flag;
117  MPI_Iprobe(source,0,MPI_COMM_WORLD,&flag,&status);
118  return flag;
119 }
120 
122 
130 {
131  check_init();
132  int retval;
133  MPI_Comm_size(MPI_COMM_WORLD,&retval);
134  return retval;
135 }
136 
138 
146 {
147  check_init();
148  int retval;
149  MPI_Comm_rank(MPI_COMM_WORLD,&retval);
150  return retval;
151 }
152 
154 
163 {
164  check_init();
165  return m_multithread;
166 }
167 
168 void mpi_environment::listen()
169 {
170  std::pair<boost::shared_ptr<population>,algorithm::base_ptr> payload;
171  while (true) {
172  // Receive the payload from the master.
173  recv(payload,0);
174  // If the payload is empty, it is the shutdown payload.
175  if (payload.first.get() == 0) {
176  pagmo_assert(payload.second.get() == 0);
177  break;
178  }
179 
180  // Create copy of data received
181  const boost::shared_ptr<population> pop_copy(new population(*payload.first));
182  try {
183  // Perform the evolution.
184  payload.second->evolve(*payload.first);
185  } catch (const std::exception &e) {
186  std::cout << "MPI Remote Error during island evolution using " << payload.second->get_name() << ": " << e.what() << std::endl;
187  } catch (...) {
188  std::cout << "MPI Remote Error during island evolution using " << payload.second->get_name() << ", unknown exception caught. :(" << std::endl;
189  }
190 
191  try {
192  // Send back to the master the evolved population.
193  send(payload.first,0);
194  } catch (const boost::archive::archive_exception &e) {
195  std::cout << "MPI Send Error during island evolution using " << payload.second->get_name() << ": " << e.what() << std::endl;
196  // Send back to the master the original population.
197  send(pop_copy,0);
198  } catch (...) {
199  std::cout << "MPI Send Error during island evolution using " << payload.second->get_name() << ", unknown exception caught. :(" << std::endl;
200  // Send back to the master the original population.
201  send(pop_copy,0);
202  }
203  }
204  // Destroy the MPI environment before exiting.
205  MPI_Finalize();
206  std::exit(0);
207 }
208 
209 }
boost::shared_ptr< base > base_ptr
Alias for shared pointer to base algorithm.
static bool is_multithread()
Thread-safety of the MPI implementation.
static void recv(T &retval, int source)
Receive MPI payload.
Root PaGMO namespace.
static bool iprobe(int)
Probe for message.
static int get_size()
MPI world size.
~mpi_environment()
Destructor.
static void send(const T &payload, int destination)
Send MPI payload.
mpi_environment()
Default constructor.
static int get_rank()
MPI rank.