/Src/Dependencies/Boost/boost/graph/distributed/detail/mpi_process_group.ipp

http://hadesmem.googlecode.com/ · C++ Header · 1007 lines · 707 code · 183 blank · 117 comment · 115 complexity · c8c5218e8118e9162ba9761569ab4bc6 MD5 · raw file

  1. // -*- C++ -*-
  2. // Copyright (C) 2004-2008 The Trustees of Indiana University.
  3. // Copyright (C) 2007 Douglas Gregor <doug.gregor@gmail.com>
  4. // Copyright (C) 2007 Matthias Troyer <troyer@boost-consulting.com>
  5. // Use, modification and distribution is subject to the Boost Software
  6. // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  7. // http://www.boost.org/LICENSE_1_0.txt)
  8. // Authors: Douglas Gregor
  9. // Andrew Lumsdaine
  10. // Matthias Troyer
  11. //#define PBGL_PROCESS_GROUP_DEBUG
  12. #ifndef BOOST_GRAPH_USE_MPI
  13. #error "Parallel BGL files should not be included unless <boost/graph/use_mpi.hpp> has been included"
  14. #endif
  15. #include <boost/assert.hpp>
  16. #include <algorithm>
  17. #include <boost/graph/parallel/detail/untracked_pair.hpp>
  18. #include <numeric>
  19. #include <iterator>
  20. #include <functional>
  21. #include <vector>
  22. #include <queue>
  23. #include <stack>
  24. #include <boost/graph/distributed/detail/tag_allocator.hpp>
  25. #include <stdio.h>
  26. // #define PBGL_PROCESS_GROUP_DEBUG
  27. #ifdef PBGL_PROCESS_GROUP_DEBUG
  28. # include <iostream>
  29. #endif
  30. namespace boost { namespace graph { namespace distributed {
  31. struct mpi_process_group::impl
  32. {
  33. typedef mpi_process_group::message_header message_header;
  34. typedef mpi_process_group::outgoing_messages outgoing_messages;
  35. /**
  36. * Stores the incoming messages from a particular processor.
  37. *
  38. * @todo Evaluate whether we should use a deque instance, which
  39. * would reduce could reduce the cost of "receiving" messages and
  40. allow us to deallocate memory earlier, but increases the time
  41. spent in the synchronization step.
  42. */
  43. struct incoming_messages {
  44. incoming_messages();
  45. ~incoming_messages() {}
  46. std::vector<message_header> headers;
  47. buffer_type buffer;
  48. std::vector<std::vector<message_header>::iterator> next_header;
  49. };
  50. struct batch_request {
  51. MPI_Request request;
  52. buffer_type buffer;
  53. };
  54. // send once we have a certain number of messages or bytes in the buffer
  55. // these numbers need to be tuned, we keep them small at first for testing
  56. std::size_t batch_header_number;
  57. std::size_t batch_buffer_size;
  58. std::size_t batch_message_size;
  59. /**
  60. * The actual MPI communicator used to transmit data.
  61. */
  62. boost::mpi::communicator comm;
  63. /**
  64. * The MPI communicator used to transmit out-of-band replies.
  65. */
  66. boost::mpi::communicator oob_reply_comm;
  67. /// Outgoing message information, indexed by destination processor.
  68. std::vector<outgoing_messages> outgoing;
  69. /// Incoming message information, indexed by source processor.
  70. std::vector<incoming_messages> incoming;
  71. /// The numbers of processors that have entered a synchronization stage
  72. std::vector<int> processors_synchronizing_stage;
  73. /// The synchronization stage of a processor
  74. std::vector<int> synchronizing_stage;
  75. /// Number of processors still sending messages
  76. std::vector<int> synchronizing_unfinished;
  77. /// Number of batches sent since last synchronization stage
  78. std::vector<int> number_sent_batches;
  79. /// Number of batches received minus number of expected batches
  80. std::vector<int> number_received_batches;
  81. /// The context of the currently-executing trigger, or @c trc_none
  82. /// if no trigger is executing.
  83. trigger_receive_context trigger_context;
  84. /// Non-zero indicates that we're processing batches
  85. /// Increment this when processing patches,
  86. /// decrement it when you're done.
  87. int processing_batches;
  88. /**
  89. * Contains all of the active blocks corresponding to attached
  90. * distributed data structures.
  91. */
  92. blocks_type blocks;
  93. /// Whether we are currently synchronizing
  94. bool synchronizing;
  95. /// The MPI requests for posted sends of oob messages
  96. std::vector<MPI_Request> requests;
  97. /// The MPI buffers for posted irecvs of oob messages
  98. std::map<int,buffer_type> buffers;
  99. /// Queue for message batches received while already processing messages
  100. std::queue<std::pair<int,outgoing_messages> > new_batches;
  101. /// Maximum encountered size of the new_batches queue
  102. std::size_t max_received;
  103. /// The MPI requests and buffers for batchess being sent
  104. std::list<batch_request> sent_batches;
  105. /// Maximum encountered size of the sent_batches list
  106. std::size_t max_sent;
  107. /// Pre-allocated requests in a pool
  108. std::vector<batch_request> batch_pool;
  109. /// A stack controlling which batches are available
  110. std::stack<std::size_t> free_batches;
  111. void free_sent_batches();
  112. // Tag allocator
  113. detail::tag_allocator allocated_tags;
  114. impl(std::size_t num_headers, std::size_t buffers_size,
  115. communicator_type parent_comm);
  116. ~impl();
  117. private:
  118. void set_batch_size(std::size_t header_num, std::size_t buffer_sz);
  119. };
  120. inline trigger_receive_context mpi_process_group::trigger_context() const
  121. {
  122. return impl_->trigger_context;
  123. }
  124. template<typename T>
  125. void
  126. mpi_process_group::send_impl(int dest, int tag, const T& value,
  127. mpl::true_ /*is_mpi_datatype*/) const
  128. {
  129. BOOST_ASSERT(tag < msg_reserved_first || tag > msg_reserved_last);
  130. impl::outgoing_messages& outgoing = impl_->outgoing[dest];
  131. // Start constructing the message header
  132. impl::message_header header;
  133. header.source = process_id(*this);
  134. header.tag = tag;
  135. header.offset = outgoing.buffer.size();
  136. boost::mpi::packed_oarchive oa(impl_->comm, outgoing.buffer);
  137. oa << value;
  138. #ifdef PBGL_PROCESS_GROUP_DEBUG
  139. std::cerr << "SEND: " << process_id(*this) << " -> " << dest << ", tag = "
  140. << tag << ", bytes = " << packed_size << std::endl;
  141. #endif
  142. // Store the header
  143. header.bytes = outgoing.buffer.size() - header.offset;
  144. outgoing.headers.push_back(header);
  145. maybe_send_batch(dest);
  146. }
  147. template<typename T>
  148. void
  149. mpi_process_group::send_impl(int dest, int tag, const T& value,
  150. mpl::false_ /*is_mpi_datatype*/) const
  151. {
  152. BOOST_ASSERT(tag < msg_reserved_first || tag > msg_reserved_last);
  153. impl::outgoing_messages& outgoing = impl_->outgoing[dest];
  154. // Start constructing the message header
  155. impl::message_header header;
  156. header.source = process_id(*this);
  157. header.tag = tag;
  158. header.offset = outgoing.buffer.size();
  159. // Serialize into the buffer
  160. boost::mpi::packed_oarchive out(impl_->comm, outgoing.buffer);
  161. out << value;
  162. // Store the header
  163. header.bytes = outgoing.buffer.size() - header.offset;
  164. outgoing.headers.push_back(header);
  165. maybe_send_batch(dest);
  166. #ifdef PBGL_PROCESS_GROUP_DEBUG
  167. std::cerr << "SEND: " << process_id(*this) << " -> " << dest << ", tag = "
  168. << tag << ", bytes = " << header.bytes << std::endl;
  169. #endif
  170. }
  171. template<typename T>
  172. inline void
  173. send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
  174. int tag, const T& value)
  175. {
  176. pg.send_impl(dest, pg.encode_tag(pg.my_block_number(), tag), value,
  177. boost::mpi::is_mpi_datatype<T>());
  178. }
  179. template<typename T>
  180. typename enable_if<boost::mpi::is_mpi_datatype<T>, void>::type
  181. send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
  182. int tag, const T values[], std::size_t n)
  183. {
  184. pg.send_impl(dest, pg.encode_tag(pg.my_block_number(), tag),
  185. boost::serialization::make_array(values,n),
  186. boost::mpl::true_());
  187. }
  188. template<typename T>
  189. typename disable_if<boost::mpi::is_mpi_datatype<T>, void>::type
  190. mpi_process_group::
  191. array_send_impl(int dest, int tag, const T values[], std::size_t n) const
  192. {
  193. BOOST_ASSERT(tag < msg_reserved_first || tag > msg_reserved_last);
  194. impl::outgoing_messages& outgoing = impl_->outgoing[dest];
  195. // Start constructing the message header
  196. impl::message_header header;
  197. header.source = process_id(*this);
  198. header.tag = tag;
  199. header.offset = outgoing.buffer.size();
  200. // Serialize into the buffer
  201. boost::mpi::packed_oarchive out(impl_->comm, outgoing.buffer);
  202. out << n;
  203. for (std::size_t i = 0; i < n; ++i)
  204. out << values[i];
  205. // Store the header
  206. header.bytes = outgoing.buffer.size() - header.offset;
  207. outgoing.headers.push_back(header);
  208. maybe_send_batch(dest);
  209. #ifdef PBGL_PROCESS_GROUP_DEBUG
  210. std::cerr << "SEND: " << process_id(*this) << " -> " << dest << ", tag = "
  211. << tag << ", bytes = " << header.bytes << std::endl;
  212. #endif
  213. }
  214. template<typename T>
  215. typename disable_if<boost::mpi::is_mpi_datatype<T>, void>::type
  216. send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
  217. int tag, const T values[], std::size_t n)
  218. {
  219. pg.array_send_impl(dest, pg.encode_tag(pg.my_block_number(), tag),
  220. values, n);
  221. }
  222. template<typename InputIterator>
  223. void
  224. send(const mpi_process_group& pg, mpi_process_group::process_id_type dest,
  225. int tag, InputIterator first, InputIterator last)
  226. {
  227. typedef typename std::iterator_traits<InputIterator>::value_type value_type;
  228. std::vector<value_type> values(first, last);
  229. if (values.empty()) send(pg, dest, tag, static_cast<value_type*>(0), 0);
  230. else send(pg, dest, tag, &values[0], values.size());
  231. }
  232. template<typename T>
  233. bool
  234. mpi_process_group::receive_impl(int source, int tag, T& value,
  235. mpl::true_ /*is_mpi_datatype*/) const
  236. {
  237. #ifdef PBGL_PROCESS_GROUP_DEBUG
  238. std::cerr << "RECV: " << process_id(*this) << " <- " << source << ", tag = "
  239. << tag << std::endl;
  240. #endif
  241. impl::incoming_messages& incoming = impl_->incoming[source];
  242. // Find the next header with the right tag
  243. std::vector<impl::message_header>::iterator header =
  244. incoming.next_header[my_block_number()];
  245. while (header != incoming.headers.end() && header->tag != tag) ++header;
  246. // If no header is found, notify the caller
  247. if (header == incoming.headers.end()) return false;
  248. // Unpack the data
  249. if (header->bytes > 0) {
  250. boost::mpi::packed_iarchive ia(impl_->comm, incoming.buffer,
  251. archive::no_header, header->offset);
  252. ia >> value;
  253. }
  254. // Mark this message as received
  255. header->tag = -1;
  256. // Move the "next header" indicator to the next unreceived message
  257. while (incoming.next_header[my_block_number()] != incoming.headers.end()
  258. && incoming.next_header[my_block_number()]->tag == -1)
  259. ++incoming.next_header[my_block_number()];
  260. if (incoming.next_header[my_block_number()] == incoming.headers.end()) {
  261. bool finished = true;
  262. for (std::size_t i = 0; i < incoming.next_header.size() && finished; ++i) {
  263. if (incoming.next_header[i] != incoming.headers.end()) finished = false;
  264. }
  265. if (finished) {
  266. std::vector<impl::message_header> no_headers;
  267. incoming.headers.swap(no_headers);
  268. buffer_type empty_buffer;
  269. incoming.buffer.swap(empty_buffer);
  270. for (std::size_t i = 0; i < incoming.next_header.size(); ++i)
  271. incoming.next_header[i] = incoming.headers.end();
  272. }
  273. }
  274. return true;
  275. }
  276. template<typename T>
  277. bool
  278. mpi_process_group::receive_impl(int source, int tag, T& value,
  279. mpl::false_ /*is_mpi_datatype*/) const
  280. {
  281. impl::incoming_messages& incoming = impl_->incoming[source];
  282. // Find the next header with the right tag
  283. std::vector<impl::message_header>::iterator header =
  284. incoming.next_header[my_block_number()];
  285. while (header != incoming.headers.end() && header->tag != tag) ++header;
  286. // If no header is found, notify the caller
  287. if (header == incoming.headers.end()) return false;
  288. // Deserialize the data
  289. boost::mpi::packed_iarchive in(impl_->comm, incoming.buffer,
  290. archive::no_header, header->offset);
  291. in >> value;
  292. // Mark this message as received
  293. header->tag = -1;
  294. // Move the "next header" indicator to the next unreceived message
  295. while (incoming.next_header[my_block_number()] != incoming.headers.end()
  296. && incoming.next_header[my_block_number()]->tag == -1)
  297. ++incoming.next_header[my_block_number()];
  298. if (incoming.next_header[my_block_number()] == incoming.headers.end()) {
  299. bool finished = true;
  300. for (std::size_t i = 0; i < incoming.next_header.size() && finished; ++i) {
  301. if (incoming.next_header[i] != incoming.headers.end()) finished = false;
  302. }
  303. if (finished) {
  304. std::vector<impl::message_header> no_headers;
  305. incoming.headers.swap(no_headers);
  306. buffer_type empty_buffer;
  307. incoming.buffer.swap(empty_buffer);
  308. for (std::size_t i = 0; i < incoming.next_header.size(); ++i)
  309. incoming.next_header[i] = incoming.headers.end();
  310. }
  311. }
  312. return true;
  313. }
  314. template<typename T>
  315. typename disable_if<boost::mpi::is_mpi_datatype<T>, bool>::type
  316. mpi_process_group::
  317. array_receive_impl(int source, int tag, T* values, std::size_t& n) const
  318. {
  319. impl::incoming_messages& incoming = impl_->incoming[source];
  320. // Find the next header with the right tag
  321. std::vector<impl::message_header>::iterator header =
  322. incoming.next_header[my_block_number()];
  323. while (header != incoming.headers.end() && header->tag != tag) ++header;
  324. // If no header is found, notify the caller
  325. if (header == incoming.headers.end()) return false;
  326. // Deserialize the data
  327. boost::mpi::packed_iarchive in(impl_->comm, incoming.buffer,
  328. archive::no_header, header->offset);
  329. std::size_t num_sent;
  330. in >> num_sent;
  331. if (num_sent > n)
  332. std::cerr << "ERROR: Have " << num_sent << " items but only space for "
  333. << n << " items\n";
  334. for (std::size_t i = 0; i < num_sent; ++i)
  335. in >> values[i];
  336. n = num_sent;
  337. // Mark this message as received
  338. header->tag = -1;
  339. // Move the "next header" indicator to the next unreceived message
  340. while (incoming.next_header[my_block_number()] != incoming.headers.end()
  341. && incoming.next_header[my_block_number()]->tag == -1)
  342. ++incoming.next_header[my_block_number()];
  343. if (incoming.next_header[my_block_number()] == incoming.headers.end()) {
  344. bool finished = true;
  345. for (std::size_t i = 0; i < incoming.next_header.size() && finished; ++i) {
  346. if (incoming.next_header[i] != incoming.headers.end()) finished = false;
  347. }
  348. if (finished) {
  349. std::vector<impl::message_header> no_headers;
  350. incoming.headers.swap(no_headers);
  351. buffer_type empty_buffer;
  352. incoming.buffer.swap(empty_buffer);
  353. for (std::size_t i = 0; i < incoming.next_header.size(); ++i)
  354. incoming.next_header[i] = incoming.headers.end();
  355. }
  356. }
  357. return true;
  358. }
  359. // Construct triggers
  360. template<typename Type, typename Handler>
  361. void mpi_process_group::trigger(int tag, const Handler& handler)
  362. {
  363. BOOST_ASSERT(block_num);
  364. install_trigger(tag,my_block_number(),shared_ptr<trigger_base>(
  365. new trigger_launcher<Type, Handler>(*this, tag, handler)));
  366. }
  367. template<typename Type, typename Handler>
  368. void mpi_process_group::trigger_with_reply(int tag, const Handler& handler)
  369. {
  370. BOOST_ASSERT(block_num);
  371. install_trigger(tag,my_block_number(),shared_ptr<trigger_base>(
  372. new reply_trigger_launcher<Type, Handler>(*this, tag, handler)));
  373. }
  374. template<typename Type, typename Handler>
  375. void mpi_process_group::global_trigger(int tag, const Handler& handler,
  376. std::size_t sz)
  377. {
  378. if (sz==0) // normal trigger
  379. install_trigger(tag,0,shared_ptr<trigger_base>(
  380. new global_trigger_launcher<Type, Handler>(*this, tag, handler)));
  381. else // trigger with irecv
  382. install_trigger(tag,0,shared_ptr<trigger_base>(
  383. new global_irecv_trigger_launcher<Type, Handler>(*this, tag, handler,sz)));
  384. }
  385. namespace detail {
  386. template<typename Type>
  387. void do_oob_receive(mpi_process_group const& self,
  388. int source, int tag, Type& data, mpl::true_ /*is_mpi_datatype*/)
  389. {
  390. using boost::mpi::get_mpi_datatype;
  391. //self.impl_->comm.recv(source,tag,data);
  392. MPI_Recv(&data, 1, get_mpi_datatype<Type>(data), source, tag, self.impl_->comm,
  393. MPI_STATUS_IGNORE);
  394. }
  395. template<typename Type>
  396. void do_oob_receive(mpi_process_group const& self,
  397. int source, int tag, Type& data, mpl::false_ /*is_mpi_datatype*/)
  398. {
  399. // self.impl_->comm.recv(source,tag,data);
  400. // Receive the size of the data packet
  401. boost::mpi::status status;
  402. status = self.impl_->comm.probe(source, tag);
  403. #if BOOST_VERSION >= 103600
  404. int size = status.count<boost::mpi::packed>().get();
  405. #else
  406. int size;
  407. MPI_Status& mpi_status = status;
  408. MPI_Get_count(&mpi_status, MPI_PACKED, &size);
  409. #endif
  410. // Receive the data packed itself
  411. boost::mpi::packed_iarchive in(self.impl_->comm);
  412. in.resize(size);
  413. MPI_Recv(in.address(), size, MPI_PACKED, source, tag, self.impl_->comm,
  414. MPI_STATUS_IGNORE);
  415. // Deserialize the data
  416. in >> data;
  417. }
  418. template<typename Type>
  419. void do_oob_receive(mpi_process_group const& self, int source, int tag, Type& data)
  420. {
  421. do_oob_receive(self, source, tag, data,
  422. boost::mpi::is_mpi_datatype<Type>());
  423. }
  424. } // namespace detail
  425. template<typename Type, typename Handler>
  426. void
  427. mpi_process_group::trigger_launcher<Type, Handler>::
  428. receive(mpi_process_group const&, int source, int tag,
  429. trigger_receive_context context, int block) const
  430. {
  431. #ifdef PBGL_PROCESS_GROUP_DEBUG
  432. std::cerr << (out_of_band? "OOB trigger" : "Trigger")
  433. << " receive from source " << source << " and tag " << tag
  434. << " in block " << (block == -1 ? self.my_block_number() : block) << std::endl;
  435. #endif
  436. Type data;
  437. if (context == trc_out_of_band) {
  438. // Receive the message directly off the wire
  439. int realtag = self.encode_tag(
  440. block == -1 ? self.my_block_number() : block, tag);
  441. detail::do_oob_receive(self,source,realtag,data);
  442. }
  443. else
  444. // Receive the message out of the local buffer
  445. boost::graph::distributed::receive(self, source, tag, data);
  446. // Pass the message off to the handler
  447. handler(source, tag, data, context);
  448. }
  449. template<typename Type, typename Handler>
  450. void
  451. mpi_process_group::reply_trigger_launcher<Type, Handler>::
  452. receive(mpi_process_group const&, int source, int tag,
  453. trigger_receive_context context, int block) const
  454. {
  455. #ifdef PBGL_PROCESS_GROUP_DEBUG
  456. std::cerr << (out_of_band? "OOB reply trigger" : "Reply trigger")
  457. << " receive from source " << source << " and tag " << tag
  458. << " in block " << (block == -1 ? self.my_block_number() : block) << std::endl;
  459. #endif
  460. BOOST_ASSERT(context == trc_out_of_band);
  461. boost::parallel::detail::untracked_pair<int, Type> data;
  462. // Receive the message directly off the wire
  463. int realtag = self.encode_tag(block == -1 ? self.my_block_number() : block,
  464. tag);
  465. detail::do_oob_receive(self, source, realtag, data);
  466. // Pass the message off to the handler and send the result back to
  467. // the source.
  468. send_oob(self, source, data.first,
  469. handler(source, tag, data.second, context), -2);
  470. }
  471. template<typename Type, typename Handler>
  472. void
  473. mpi_process_group::global_trigger_launcher<Type, Handler>::
  474. receive(mpi_process_group const& self, int source, int tag,
  475. trigger_receive_context context, int block) const
  476. {
  477. #ifdef PBGL_PROCESS_GROUP_DEBUG
  478. std::cerr << (out_of_band? "OOB trigger" : "Trigger")
  479. << " receive from source " << source << " and tag " << tag
  480. << " in block " << (block == -1 ? self.my_block_number() : block) << std::endl;
  481. #endif
  482. Type data;
  483. if (context == trc_out_of_band) {
  484. // Receive the message directly off the wire
  485. int realtag = self.encode_tag(
  486. block == -1 ? self.my_block_number() : block, tag);
  487. detail::do_oob_receive(self,source,realtag,data);
  488. }
  489. else
  490. // Receive the message out of the local buffer
  491. boost::graph::distributed::receive(self, source, tag, data);
  492. // Pass the message off to the handler
  493. handler(self, source, tag, data, context);
  494. }
  495. template<typename Type, typename Handler>
  496. void
  497. mpi_process_group::global_irecv_trigger_launcher<Type, Handler>::
  498. receive(mpi_process_group const& self, int source, int tag,
  499. trigger_receive_context context, int block) const
  500. {
  501. #ifdef PBGL_PROCESS_GROUP_DEBUG
  502. std::cerr << (out_of_band? "OOB trigger" : "Trigger")
  503. << " receive from source " << source << " and tag " << tag
  504. << " in block " << (block == -1 ? self.my_block_number() : block) << std::endl;
  505. #endif
  506. Type data;
  507. if (context == trc_out_of_band) {
  508. return;
  509. }
  510. BOOST_ASSERT (context == trc_irecv_out_of_band);
  511. // force posting of new MPI_Irecv, even though buffer is already allocated
  512. boost::mpi::packed_iarchive ia(self.impl_->comm,self.impl_->buffers[tag]);
  513. ia >> data;
  514. // Start a new receive
  515. prepare_receive(self,tag,true);
  516. // Pass the message off to the handler
  517. handler(self, source, tag, data, context);
  518. }
  519. template<typename Type, typename Handler>
  520. void
  521. mpi_process_group::global_irecv_trigger_launcher<Type, Handler>::
  522. prepare_receive(mpi_process_group const& self, int tag, bool force) const
  523. {
  524. #ifdef PBGL_PROCESS_GROUP_DEBUG
  525. std::cerr << ("Posting Irecv for trigger")
  526. << " receive with tag " << tag << std::endl;
  527. #endif
  528. if (self.impl_->buffers.find(tag) == self.impl_->buffers.end()) {
  529. self.impl_->buffers[tag].resize(buffer_size);
  530. force = true;
  531. }
  532. BOOST_ASSERT(static_cast<int>(self.impl_->buffers[tag].size()) >= buffer_size);
  533. //BOOST_MPL_ASSERT(mpl::not_<is_mpi_datatype<Type> >);
  534. if (force) {
  535. self.impl_->requests.push_back(MPI_Request());
  536. MPI_Request* request = &self.impl_->requests.back();
  537. MPI_Irecv(&self.impl_->buffers[tag].front(),buffer_size,
  538. MPI_PACKED,MPI_ANY_SOURCE,tag,self.impl_->comm,request);
  539. }
  540. }
  541. template<typename T>
  542. inline mpi_process_group::process_id_type
  543. receive(const mpi_process_group& pg, int tag, T& value)
  544. {
  545. for (std::size_t source = 0; source < pg.impl_->incoming.size(); ++source) {
  546. if (pg.receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
  547. value, boost::mpi::is_mpi_datatype<T>()))
  548. return source;
  549. }
  550. BOOST_ASSERT (false);
  551. }
  552. template<typename T>
  553. typename
  554. enable_if<boost::mpi::is_mpi_datatype<T>,
  555. std::pair<mpi_process_group::process_id_type, std::size_t> >::type
  556. receive(const mpi_process_group& pg, int tag, T values[], std::size_t n)
  557. {
  558. for (std::size_t source = 0; source < pg.impl_->incoming.size(); ++source) {
  559. bool result =
  560. pg.receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
  561. boost::serialization::make_array(values,n),
  562. boost::mpl::true_());
  563. if (result)
  564. return std::make_pair(source, n);
  565. }
  566. BOOST_ASSERT(false);
  567. }
  568. template<typename T>
  569. typename
  570. disable_if<boost::mpi::is_mpi_datatype<T>,
  571. std::pair<mpi_process_group::process_id_type, std::size_t> >::type
  572. receive(const mpi_process_group& pg, int tag, T values[], std::size_t n)
  573. {
  574. for (std::size_t source = 0; source < pg.impl_->incoming.size(); ++source) {
  575. if (pg.array_receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
  576. values, n))
  577. return std::make_pair(source, n);
  578. }
  579. BOOST_ASSERT(false);
  580. }
  581. template<typename T>
  582. mpi_process_group::process_id_type
  583. receive(const mpi_process_group& pg,
  584. mpi_process_group::process_id_type source, int tag, T& value)
  585. {
  586. if (pg.receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
  587. value, boost::mpi::is_mpi_datatype<T>()))
  588. return source;
  589. else {
  590. fprintf(stderr,
  591. "Process %d failed to receive a message from process %d with tag %d in block %d.\n",
  592. process_id(pg), source, tag, pg.my_block_number());
  593. BOOST_ASSERT(false);
  594. exit(1);
  595. }
  596. }
  597. template<typename T>
  598. typename
  599. enable_if<boost::mpi::is_mpi_datatype<T>,
  600. std::pair<mpi_process_group::process_id_type, std::size_t> >::type
  601. receive(const mpi_process_group& pg, int source, int tag, T values[],
  602. std::size_t n)
  603. {
  604. if (pg.receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
  605. boost::serialization::make_array(values,n),
  606. boost::mpl::true_()))
  607. return std::make_pair(source,n);
  608. else {
  609. fprintf(stderr,
  610. "Process %d failed to receive a message from process %d with tag %d in block %d.\n",
  611. process_id(pg), source, tag, pg.my_block_number());
  612. BOOST_ASSERT(false);
  613. exit(1);
  614. }
  615. }
  616. template<typename T>
  617. typename
  618. disable_if<boost::mpi::is_mpi_datatype<T>,
  619. std::pair<mpi_process_group::process_id_type, std::size_t> >::type
  620. receive(const mpi_process_group& pg, int source, int tag, T values[],
  621. std::size_t n)
  622. {
  623. pg.array_receive_impl(source, pg.encode_tag(pg.my_block_number(), tag),
  624. values, n);
  625. return std::make_pair(source, n);
  626. }
  627. template<typename T, typename BinaryOperation>
  628. T*
  629. all_reduce(const mpi_process_group& pg, T* first, T* last, T* out,
  630. BinaryOperation bin_op)
  631. {
  632. synchronize(pg);
  633. bool inplace = first == out;
  634. if (inplace) out = new T [last-first];
  635. boost::mpi::all_reduce(boost::mpi::communicator(communicator(pg),
  636. boost::mpi::comm_attach),
  637. first, last-first, out, bin_op);
  638. if (inplace) {
  639. std::copy(out, out + (last-first), first);
  640. delete [] out;
  641. return last;
  642. }
  643. return out;
  644. }
  645. template<typename T>
  646. void
  647. broadcast(const mpi_process_group& pg, T& val,
  648. mpi_process_group::process_id_type root)
  649. {
  650. // broadcast the seed
  651. boost::mpi::communicator comm(communicator(pg),boost::mpi::comm_attach);
  652. boost::mpi::broadcast(comm,val,root);
  653. }
  654. template<typename T, typename BinaryOperation>
  655. T*
  656. scan(const mpi_process_group& pg, T* first, T* last, T* out,
  657. BinaryOperation bin_op)
  658. {
  659. synchronize(pg);
  660. bool inplace = first == out;
  661. if (inplace) out = new T [last-first];
  662. boost::mpi::scan(communicator(pg), first, last-first, out, bin_op);
  663. if (inplace) {
  664. std::copy(out, out + (last-first), first);
  665. delete [] out;
  666. return last;
  667. }
  668. return out;
  669. }
  670. template<typename InputIterator, typename T>
  671. void
  672. all_gather(const mpi_process_group& pg, InputIterator first,
  673. InputIterator last, std::vector<T>& out)
  674. {
  675. synchronize(pg);
  676. // Stick a copy of the local values into a vector, so we can broadcast it
  677. std::vector<T> local_values(first, last);
  678. // Collect the number of vertices stored in each process
  679. int size = local_values.size();
  680. std::vector<int> sizes(num_processes(pg));
  681. int result = MPI_Allgather(&size, 1, MPI_INT,
  682. &sizes[0], 1, MPI_INT,
  683. communicator(pg));
  684. BOOST_ASSERT(result == MPI_SUCCESS);
  685. // Adjust sizes based on the number of bytes
  686. std::transform(sizes.begin(), sizes.end(), sizes.begin(),
  687. std::bind2nd(std::multiplies<int>(), sizeof(T)));
  688. // Compute displacements
  689. std::vector<int> displacements;
  690. displacements.reserve(sizes.size() + 1);
  691. displacements.push_back(0);
  692. std::partial_sum(sizes.begin(), sizes.end(),
  693. std::back_inserter(displacements));
  694. // Gather all of the values
  695. out.resize(displacements.back() / sizeof(T));
  696. if (!out.empty()) {
  697. result = MPI_Allgatherv(local_values.empty()? (void*)&local_values
  698. /* local results */: (void*)&local_values[0],
  699. local_values.size() * sizeof(T),
  700. MPI_BYTE,
  701. &out[0], &sizes[0], &displacements[0], MPI_BYTE,
  702. communicator(pg));
  703. }
  704. BOOST_ASSERT(result == MPI_SUCCESS);
  705. }
  706. template<typename InputIterator>
  707. mpi_process_group
  708. process_subgroup(const mpi_process_group& pg,
  709. InputIterator first, InputIterator last)
  710. {
  711. /*
  712. boost::mpi::group current_group = communicator(pg).group();
  713. boost::mpi::group new_group = current_group.include(first,last);
  714. boost::mpi::communicator new_comm(communicator(pg),new_group);
  715. return mpi_process_group(new_comm);
  716. */
  717. std::vector<int> ranks(first, last);
  718. MPI_Group current_group;
  719. int result = MPI_Comm_group(communicator(pg), &current_group);
  720. BOOST_ASSERT(result == MPI_SUCCESS);
  721. MPI_Group new_group;
  722. result = MPI_Group_incl(current_group, ranks.size(), &ranks[0], &new_group);
  723. BOOST_ASSERT(result == MPI_SUCCESS);
  724. MPI_Comm new_comm;
  725. result = MPI_Comm_create(communicator(pg), new_group, &new_comm);
  726. BOOST_ASSERT(result == MPI_SUCCESS);
  727. result = MPI_Group_free(&new_group);
  728. BOOST_ASSERT(result == MPI_SUCCESS);
  729. result = MPI_Group_free(&current_group);
  730. BOOST_ASSERT(result == MPI_SUCCESS);
  731. if (new_comm != MPI_COMM_NULL) {
  732. mpi_process_group result_pg(boost::mpi::communicator(new_comm,boost::mpi::comm_attach));
  733. result = MPI_Comm_free(&new_comm);
  734. BOOST_ASSERT(result == 0);
  735. return result_pg;
  736. } else {
  737. return mpi_process_group(mpi_process_group::create_empty());
  738. }
  739. }
  740. template<typename Receiver>
  741. Receiver* mpi_process_group::get_receiver()
  742. {
  743. return impl_->blocks[my_block_number()]->on_receive
  744. .template target<Receiver>();
  745. }
  746. template<typename T>
  747. typename enable_if<boost::mpi::is_mpi_datatype<T> >::type
  748. receive_oob(const mpi_process_group& pg,
  749. mpi_process_group::process_id_type source, int tag, T& value, int block)
  750. {
  751. using boost::mpi::get_mpi_datatype;
  752. // Determine the actual message we expect to receive, and which
  753. // communicator it will come by.
  754. std::pair<boost::mpi::communicator, int> actual
  755. = pg.actual_communicator_and_tag(tag, block);
  756. // Post a non-blocking receive that waits until we complete this request.
  757. MPI_Request request;
  758. MPI_Irecv(&value, 1, get_mpi_datatype<T>(value),
  759. source, actual.second, actual.first, &request);
  760. int done = 0;
  761. do {
  762. MPI_Test(&request, &done, MPI_STATUS_IGNORE);
  763. if (!done)
  764. pg.poll(/*wait=*/false, block);
  765. } while (!done);
  766. }
  767. template<typename T>
  768. typename disable_if<boost::mpi::is_mpi_datatype<T> >::type
  769. receive_oob(const mpi_process_group& pg,
  770. mpi_process_group::process_id_type source, int tag, T& value, int block)
  771. {
  772. // Determine the actual message we expect to receive, and which
  773. // communicator it will come by.
  774. std::pair<boost::mpi::communicator, int> actual
  775. = pg.actual_communicator_and_tag(tag, block);
  776. boost::optional<boost::mpi::status> status;
  777. do {
  778. status = actual.first.iprobe(source, actual.second);
  779. if (!status)
  780. pg.poll();
  781. } while (!status);
  782. //actual.first.recv(status->source(), status->tag(),value);
  783. // Allocate the receive buffer
  784. boost::mpi::packed_iarchive in(actual.first);
  785. #if BOOST_VERSION >= 103600
  786. in.resize(status->count<boost::mpi::packed>().get());
  787. #else
  788. int size;
  789. MPI_Status mpi_status = *status;
  790. MPI_Get_count(&mpi_status, MPI_PACKED, &size);
  791. in.resize(size);
  792. #endif
  793. // Receive the message data
  794. MPI_Recv(in.address(), in.size(), MPI_PACKED,
  795. status->source(), status->tag(), actual.first, MPI_STATUS_IGNORE);
  796. // Unpack the message data
  797. in >> value;
  798. }
  799. template<typename SendT, typename ReplyT>
  800. typename enable_if<boost::mpi::is_mpi_datatype<ReplyT> >::type
  801. send_oob_with_reply(const mpi_process_group& pg,
  802. mpi_process_group::process_id_type dest,
  803. int tag, const SendT& send_value, ReplyT& reply_value,
  804. int block)
  805. {
  806. detail::tag_allocator::token reply_tag = pg.impl_->allocated_tags.get_tag();
  807. send_oob(pg, dest, tag, boost::parallel::detail::make_untracked_pair(
  808. (int)reply_tag, send_value), block);
  809. receive_oob(pg, dest, reply_tag, reply_value);
  810. }
  811. template<typename SendT, typename ReplyT>
  812. typename disable_if<boost::mpi::is_mpi_datatype<ReplyT> >::type
  813. send_oob_with_reply(const mpi_process_group& pg,
  814. mpi_process_group::process_id_type dest,
  815. int tag, const SendT& send_value, ReplyT& reply_value,
  816. int block)
  817. {
  818. detail::tag_allocator::token reply_tag = pg.impl_->allocated_tags.get_tag();
  819. send_oob(pg, dest, tag,
  820. boost::parallel::detail::make_untracked_pair((int)reply_tag,
  821. send_value), block);
  822. receive_oob(pg, dest, reply_tag, reply_value);
  823. }
  824. } } } // end namespace boost::graph::distributed