From 97e9366a2f79e8892f347e0bfeb44ae4f2bfcf69 Mon Sep 17 00:00:00 2001 From: Matthias Troyer Date: Thu, 26 Jun 2008 19:25:44 +0000 Subject: [PATCH] Moved Serialization and MPI fixes and updates from trunk to release branch [SVN r46743] --- build/Jamfile.v2 | 1 + build/__init__.py | 4 +- doc/mpi.qbk | 3 + include/boost/mpi/communicator.hpp | 18 +- include/boost/mpi/config.hpp | 5 + include/boost/mpi/datatype.hpp | 26 +- include/boost/mpi/datatype_fwd.hpp | 2 + .../mpi/detail/binary_buffer_iprimitive.hpp | 122 +++++++++ .../mpi/detail/binary_buffer_oprimitive.hpp | 103 +++++++ include/boost/mpi/detail/content_oarchive.hpp | 2 +- .../boost/mpi/detail/forward_iprimitive.hpp | 26 +- .../boost/mpi/detail/forward_oprimitive.hpp | 26 +- .../mpi/detail/forward_skeleton_iarchive.hpp | 25 +- .../mpi/detail/forward_skeleton_oarchive.hpp | 25 +- .../mpi/detail/ignore_skeleton_oarchive.hpp | 17 +- .../boost/mpi/detail/mpi_datatype_cache.hpp | 8 +- .../mpi/detail/mpi_datatype_oarchive.hpp | 1 + .../boost/mpi/detail/packed_iprimitive.hpp | 15 +- .../mpi/detail/text_skeleton_oarchive.hpp | 5 +- include/boost/mpi/packed_iarchive.hpp | 46 +++- include/boost/mpi/packed_oarchive.hpp | 37 ++- include/boost/mpi/skeleton_and_content.hpp | 3 + include/boost/mpi/status.hpp | 2 + src/content_oarchive.cpp | 3 +- src/mpi_datatype_cache.cpp | 35 ++- src/packed_skeleton_iarchive.cpp | 4 +- src/packed_skeleton_oarchive.cpp | 3 + src/point_to_point.cpp | 2 +- src/python/documentation.cpp | 150 ++++++++--- src/python/module.cpp | 4 +- src/python/py_communicator.cpp | 13 +- src/python/py_exception.cpp | 9 +- src/python/py_nonblocking.cpp | 255 ++++++++++++++++++ src/python/py_request.cpp | 94 +++++-- src/python/py_timer.cpp | 2 +- src/python/request_with_value.hpp | 71 +++++ src/python/skeleton_and_content.cpp | 21 +- src/python/status.cpp | 2 +- test/broadcast_test.cpp | 1 - 39 files changed, 985 insertions(+), 206 deletions(-) create mode 100644 include/boost/mpi/detail/binary_buffer_iprimitive.hpp create mode 100644 include/boost/mpi/detail/binary_buffer_oprimitive.hpp create mode 100644 src/python/py_nonblocking.cpp create mode 100644 src/python/request_with_value.hpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index d753ea5..5e05319 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -82,6 +82,7 @@ libraries += boost_mpi ; python/datatypes.cpp python/documentation.cpp python/py_environment.cpp + python/py_nonblocking.cpp python/py_exception.cpp python/module.cpp python/py_request.cpp diff --git a/build/__init__.py b/build/__init__.py index 9032fdf..ffd3862 100644 --- a/build/__init__.py +++ b/build/__init__.py @@ -1,10 +1,10 @@ import sys if sys.platform == 'linux2': - import dl + import DLFCN as dl flags = sys.getdlopenflags() sys.setdlopenflags(dl.RTLD_NOW|dl.RTLD_GLOBAL) import mpi sys.setdlopenflags(flags) else: -import mpi + import mpi diff --git a/doc/mpi.qbk b/doc/mpi.qbk index 724c981..0979ee8 100644 --- a/doc/mpi.qbk +++ b/doc/mpi.qbk @@ -2025,6 +2025,9 @@ performance. [section:history Revision History] +* *Boost 1.36.0*: + * Support for non-blocking operations in Python, from Andreas Klöckner + * *Boost 1.35.0*: Initial release, containing the following post-review changes * Support for arrays in all collective operations * Support default-construction of [classref boost::mpi::environment environment] diff --git a/include/boost/mpi/communicator.hpp b/include/boost/mpi/communicator.hpp index 2bd8ec9..e450e2a 100644 --- a/include/boost/mpi/communicator.hpp +++ b/include/boost/mpi/communicator.hpp @@ -1141,7 +1141,7 @@ communicator::array_send_impl(int dest, int tag, const T* values, int n, { BOOST_MPI_CHECK_RESULT(MPI_Send, (const_cast(values), n, - get_mpi_datatype(*values), + get_mpi_datatype(*values), dest, tag, MPI_Comm(*this))); } @@ -1174,7 +1174,7 @@ status communicator::recv_impl(int source, int tag, T& value, mpl::true_) const BOOST_MPI_CHECK_RESULT(MPI_Recv, (const_cast(&value), 1, - get_mpi_datatype(value), + get_mpi_datatype(value), source, tag, MPI_Comm(*this), &stat.m_status)); return stat; } @@ -1209,7 +1209,7 @@ communicator::array_recv_impl(int source, int tag, T* values, int n, status stat; BOOST_MPI_CHECK_RESULT(MPI_Recv, (const_cast(values), n, - get_mpi_datatype(*values), + get_mpi_datatype(*values), source, tag, MPI_Comm(*this), &stat.m_status)); return stat; } @@ -1256,7 +1256,7 @@ communicator::isend_impl(int dest, int tag, const T& value, mpl::true_) const request req; BOOST_MPI_CHECK_RESULT(MPI_Isend, (const_cast(&value), 1, - get_mpi_datatype(value), + get_mpi_datatype(value), dest, tag, MPI_Comm(*this), &req.m_requests[0])); return req; } @@ -1291,7 +1291,7 @@ communicator::array_isend_impl(int dest, int tag, const T* values, int n, request req; BOOST_MPI_CHECK_RESULT(MPI_Isend, (const_cast(values), n, - get_mpi_datatype(*values), + get_mpi_datatype(*values), dest, tag, MPI_Comm(*this), &req.m_requests[0])); return req; } @@ -1531,7 +1531,7 @@ communicator::irecv_impl(int source, int tag, T& value, mpl::true_) const request req; BOOST_MPI_CHECK_RESULT(MPI_Irecv, (const_cast(&value), 1, - get_mpi_datatype(value), + get_mpi_datatype(value), source, tag, MPI_Comm(*this), &req.m_requests[0])); return req; } @@ -1548,7 +1548,7 @@ communicator::irecv_impl(int source, int tag, T& value, mpl::false_) const BOOST_MPI_CHECK_RESULT(MPI_Irecv, (&data->count, 1, - get_mpi_datatype(data->count), + get_mpi_datatype(data->count), source, tag, MPI_Comm(*this), &req.m_requests[0])); return req; @@ -1569,7 +1569,7 @@ communicator::array_irecv_impl(int source, int tag, T* values, int n, request req; BOOST_MPI_CHECK_RESULT(MPI_Irecv, (const_cast(values), n, - get_mpi_datatype(*values), + get_mpi_datatype(*values), source, tag, MPI_Comm(*this), &req.m_requests[0])); return req; } @@ -1587,7 +1587,7 @@ communicator::array_irecv_impl(int source, int tag, T* values, int n, BOOST_MPI_CHECK_RESULT(MPI_Irecv, (&data->count, 1, - get_mpi_datatype(data->count), + get_mpi_datatype(data->count), source, tag, MPI_Comm(*this), &req.m_requests[0])); return req; diff --git a/include/boost/mpi/config.hpp b/include/boost/mpi/config.hpp index 8d6d919..cf08e2c 100644 --- a/include/boost/mpi/config.hpp +++ b/include/boost/mpi/config.hpp @@ -20,6 +20,11 @@ #include #include +/** @brief Define this macro to avoid expensice MPI_Pack/Unpack calls on + * homogeneous machines. +*/ +//#define BOOST_MPI_HOMOGENEOUS + // If this is an MPI-2 implementation, define configuration macros for // the features we are interested in. #if defined(MPI_VERSION) && MPI_VERSION == 2 diff --git a/include/boost/mpi/datatype.hpp b/include/boost/mpi/datatype.hpp index fb7d5de..f929319 100644 --- a/include/boost/mpi/datatype.hpp +++ b/include/boost/mpi/datatype.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include // for std::pair @@ -198,6 +199,9 @@ template<> \ : boost::mpl::bool_ \ {} +/// INTERNAL ONLY +BOOST_MPI_DATATYPE(packed, MPI_PACKED, builtin); + /// INTERNAL ONLY BOOST_MPI_DATATYPE(char, MPI_CHAR, builtin); @@ -252,6 +256,13 @@ BOOST_MPI_DATATYPE(std::pair), MPI_SHORT_INT, BOOST_MPI_DATATYPE(std::pair), MPI_2INT, builtin); #undef BOOST_MPI_LIST2 +/// specialization of is_mpi_datatype for pairs +template +struct is_mpi_datatype > + : public mpl::and_,is_mpi_datatype > +{ +}; + // Define wchar_t specialization of is_mpi_datatype, if possible. #if !defined(BOOST_NO_INTRINSIC_WCHAR_T) && \ (defined(MPI_WCHAR) || (defined(MPI_VERSION) && MPI_VERSION >= 2)) @@ -314,11 +325,16 @@ struct is_mpi_datatype : boost::mpl::bool_ {}; -/// INTERNAL ONLY -template -struct is_mpi_datatype > - : mpl::and_, is_mpi_datatype > { }; - } } // end namespace boost::mpi +// define a macro to make explicit designation of this more transparent +#define BOOST_IS_MPI_DATATYPE(T) \ +namespace boost { \ +namespace mpi { \ +template<> \ +struct is_mpi_datatype< T > : mpl::true_ {}; \ +}} \ +/**/ + + #endif // BOOST_MPI_MPI_DATATYPE_HPP diff --git a/include/boost/mpi/datatype_fwd.hpp b/include/boost/mpi/datatype_fwd.hpp index ebf2921..20dedb5 100644 --- a/include/boost/mpi/datatype_fwd.hpp +++ b/include/boost/mpi/datatype_fwd.hpp @@ -27,6 +27,8 @@ template struct is_mpi_byte_datatype; template struct is_mpi_datatype; template MPI_Datatype get_mpi_datatype(const T& x = T()); +/// a dummy data type giving MPI_PACKED as its MPI_Datatype +struct packed {}; } } // end namespace boost::mpi #endif // BOOST_MPI_MPI_DATATYPE_FWD_HPP diff --git a/include/boost/mpi/detail/binary_buffer_iprimitive.hpp b/include/boost/mpi/detail/binary_buffer_iprimitive.hpp new file mode 100644 index 0000000..6b3a04b --- /dev/null +++ b/include/boost/mpi/detail/binary_buffer_iprimitive.hpp @@ -0,0 +1,122 @@ +// (C) Copyright 2005-2007 Matthias Troyer + +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Matthias Troyer + +#ifndef BOOST_MPI_BINARY_BUFFER_IPRIMITIVE_HPP +#define BOOST_MPI_BINARY_BUFFER_IPRIMITIVE_HPP + +#include +#include +#include // size_t +#include +#include +#include +#include +#include +#include +#include +#include +#include // for memcpy + +namespace boost { namespace mpi { + +/// deserialization using MPI_Unpack + +class BOOST_MPI_DECL binary_buffer_iprimitive +{ +public: + /// the type of the buffer from which the data is unpacked upon deserialization + typedef std::vector > buffer_type; + + binary_buffer_iprimitive(buffer_type & b, MPI_Comm const &, int position = 0) + : buffer_(b), + position(position) + { + } + + void* address () + { + return &buffer_.front(); + } + + void const* address () const + { + return &buffer_.front(); + } + + const std::size_t& size() const + { + return size_ = buffer_.size(); + } + + void resize(std::size_t s) + { + buffer_.resize(s); + } + + void load_binary(void *address, std::size_t count) + { + load_impl(address,count); + } + + // fast saving of arrays of fundamental types + template + void load_array(serialization::array const& x, unsigned int /* file_version */) + { + BOOST_MPL_ASSERT((serialization::is_bitwise_serializable::type>)); + if (x.count()) + load_impl(x.address(), sizeof(T)*x.count()); + } + + typedef serialization::is_bitwise_serializable use_array_optimization; + + template + void load(serialization::array const& x) + { + load_array(x,0u); + } + + // default saving of primitives. + template + void load( T & t) + { + BOOST_MPL_ASSERT((serialization::is_bitwise_serializable::type>)); + load_impl(&t, sizeof(T)); + } + + void load( std::string & s) + { + unsigned int l; + load(l); + // borland de-allocator fixup + #if BOOST_WORKAROUND(_RWSTD_VER, BOOST_TESTED_AT(20101)) + if(NULL != s.data()) + #endif + s.resize(l); + // note breaking a rule here - could be a problem on some platform + load_impl(const_cast(s.data()),l); + } + +private: + + void load_impl(void * p, int l) + { + if (position+l > static_cast(buffer_.size())) + std::cerr << position << " " << l << " " << buffer_.size() << "\n"; + assert(position+l<=static_cast(buffer_.size())); + std::memcpy(p,&buffer_[position],l); + position += l; + } + + buffer_type & buffer_; + mutable std::size_t size_; + int position; +}; + +} } // end namespace boost::mpi + +#endif // BOOST_MPI_PACKED_IPRIMITIVE_HPP diff --git a/include/boost/mpi/detail/binary_buffer_oprimitive.hpp b/include/boost/mpi/detail/binary_buffer_oprimitive.hpp new file mode 100644 index 0000000..4d0d80e --- /dev/null +++ b/include/boost/mpi/detail/binary_buffer_oprimitive.hpp @@ -0,0 +1,103 @@ +// (C) Copyright 2005-2007 Matthias Troyer + +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Matthias Troyer + +#ifndef BOOST_MPI_BINARY_BUFFER_OPRIMITIVE_HPP +#define BOOST_MPI_BINARY_BUFFER_OPRIMITIVE_HPP + +#include +#include +#include // size_t +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { namespace mpi { + +/// serialization using binary copy into a buffer + +class BOOST_MPI_DECL binary_buffer_oprimitive +{ +public: + /// the type of the buffer into which the data is packed upon serialization + typedef std::vector > buffer_type; + + binary_buffer_oprimitive(buffer_type & b, MPI_Comm const &) + : buffer_(b) + { + } + + void const * address() const + { + return &buffer_.front(); + } + + const std::size_t& size() const + { + return size_ = buffer_.size(); + } + + void save_binary(void const *address, std::size_t count) + { + save_impl(address,count); + } + + // fast saving of arrays + template + void save_array(serialization::array const& x, unsigned int /* file_version */) + { + + BOOST_MPL_ASSERT((serialization::is_bitwise_serializable::type>)); + if (x.count()) + save_impl(x.address(), x.count()*sizeof(T)); + } + + template + void save(serialization::array const& x) + { + save_array(x,0u); + } + + typedef serialization::is_bitwise_serializable use_array_optimization; + + // default saving of primitives. + template + void save(const T & t) + { + BOOST_MPL_ASSERT((serialization::is_bitwise_serializable::type>)); + save_impl(&t, sizeof(T)); + } + + void save(const std::string &s) + { + unsigned int l = static_cast(s.size()); + save(l); + save_impl(s.data(),s.size()); + } + +private: + + void save_impl(void const * p, int l) + { + char const* ptr = reinterpret_cast(p); + buffer_.insert(buffer_.end(),ptr,ptr+l); + } + + buffer_type& buffer_; + mutable std::size_t size_; +}; + +} } // end namespace boost::mpi + +#endif // BOOST_MPI_BINARY_BUFFER_OPRIMITIVE_HPP diff --git a/include/boost/mpi/detail/content_oarchive.hpp b/include/boost/mpi/detail/content_oarchive.hpp index 7fe46b5..9089078 100644 --- a/include/boost/mpi/detail/content_oarchive.hpp +++ b/include/boost/mpi/detail/content_oarchive.hpp @@ -61,5 +61,5 @@ const content get_content(const T& x) // required by export BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::detail::content_oarchive) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::detail::ignore_skeleton_oarchive) - +BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::mpi::detail::content_oarchive) #endif // BOOST_MPI_DETAIL_CONTENT_OARCHIVE_HPP diff --git a/include/boost/mpi/detail/forward_iprimitive.hpp b/include/boost/mpi/detail/forward_iprimitive.hpp index 67a43e7..44e7100 100644 --- a/include/boost/mpi/detail/forward_iprimitive.hpp +++ b/include/boost/mpi/detail/forward_iprimitive.hpp @@ -28,26 +28,26 @@ public: /// the type of the archive to which the loading of primitive types will be forwarded typedef ImplementationArchive implementation_archive_type; - - /// the constructor takes a reference to the implementation archive used for loading primitve types + + /// the constructor takes a reference to the implementation archive used for loading primitve types forward_iprimitive(implementation_archive_type& ar) - : implementation_archive(ar) + : implementation_archive(ar) {} - /// binary loading is forwarded to the implementation archive + /// binary loading is forwarded to the implementation archive void load_binary(void * address, std::size_t count ) - { - implementation_archive.load_binary(address,count); - } - - /// loading of arrays is forwarded to the implementation archive + { + implementation_archive.load_binary(address,count); + } + + /// loading of arrays is forwarded to the implementation archive template void load_array(serialization::array & x, unsigned int file_version ) { - implementation_archive.load_array(x,file_version); + implementation_archive.load_array(x,file_version); } - typedef typename ImplementationArchive::use_array_optimization use_array_optimization; + typedef typename ImplementationArchive::use_array_optimization use_array_optimization; #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS friend class archive::load_access; @@ -60,9 +60,9 @@ public: template void load(T & t) { - implementation_archive >> t; + implementation_archive >> t; } - + private: implementation_archive_type& implementation_archive; }; diff --git a/include/boost/mpi/detail/forward_oprimitive.hpp b/include/boost/mpi/detail/forward_oprimitive.hpp index 51824eb..cd232ae 100644 --- a/include/boost/mpi/detail/forward_oprimitive.hpp +++ b/include/boost/mpi/detail/forward_oprimitive.hpp @@ -29,26 +29,26 @@ public: /// the type of the archive to which the saving of primitive types will be forwarded typedef ImplementationArchive implementation_archive_type; - - /// the constructor takes a reference to the implementation archive used for saving primitve types + + /// the constructor takes a reference to the implementation archive used for saving primitve types forward_oprimitive(implementation_archive_type& ar) - : implementation_archive(ar) + : implementation_archive(ar) {} - /// binary saving is forwarded to the implementation archive + /// binary saving is forwarded to the implementation archive void save_binary(const void * address, std::size_t count) - { - implementation_archive.save_binary(address,count); - } - - /// saving of arrays is forwarded to the implementation archive + { + implementation_archive.save_binary(address,count); + } + + /// saving of arrays is forwarded to the implementation archive template void save_array(serialization::array const& x, unsigned int file_version ) { - implementation_archive.save_array(x,file_version); + implementation_archive.save_array(x,file_version); } - typedef typename ImplementationArchive::use_array_optimization use_array_optimization; + typedef typename ImplementationArchive::use_array_optimization use_array_optimization; #ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS friend class archive::save_access; @@ -61,9 +61,9 @@ public: template void save(const T & t) { - implementation_archive << t; + implementation_archive << t; } - + private: implementation_archive_type& implementation_archive; }; diff --git a/include/boost/mpi/detail/forward_skeleton_iarchive.hpp b/include/boost/mpi/detail/forward_skeleton_iarchive.hpp index 6ec4446..4004f40 100644 --- a/include/boost/mpi/detail/forward_skeleton_iarchive.hpp +++ b/include/boost/mpi/detail/forward_skeleton_iarchive.hpp @@ -14,26 +14,25 @@ #include #include #include -#include +#include #include -#include namespace boost { namespace mpi { namespace detail { template class forward_skeleton_iarchive - : public archive::array::iarchive + : public archive::detail::common_iarchive { public: typedef ImplementationArchive implementation_archive_type; - + forward_skeleton_iarchive(implementation_archive_type& ar) - : archive::array::iarchive(archive::no_header), - implementation_archive(ar) - { - } - + : archive::detail::common_iarchive(archive::no_header), + implementation_archive(ar) + { + } + #ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS public: #else @@ -50,12 +49,12 @@ protected: { archive::load(* this->This(), t); } - + #define BOOST_ARCHIVE_FORWARD_IMPLEMENTATION(T) \ void load_override(T & t , int) \ - { \ - implementation_archive >> t; \ - } + { \ + implementation_archive >> t; \ + } BOOST_ARCHIVE_FORWARD_IMPLEMENTATION(archive::class_id_optional_type) BOOST_ARCHIVE_FORWARD_IMPLEMENTATION(archive::version_type) diff --git a/include/boost/mpi/detail/forward_skeleton_oarchive.hpp b/include/boost/mpi/detail/forward_skeleton_oarchive.hpp index 4cf4c1c..07c0f73 100644 --- a/include/boost/mpi/detail/forward_skeleton_oarchive.hpp +++ b/include/boost/mpi/detail/forward_skeleton_oarchive.hpp @@ -14,26 +14,25 @@ #include #include #include -#include +#include #include -#include namespace boost { namespace mpi { namespace detail { template class forward_skeleton_oarchive - : public archive::array::oarchive + : public archive::detail::common_oarchive { public: typedef ImplementationArchive implementation_archive_type; - + forward_skeleton_oarchive(implementation_archive_type& ar) - : archive::array::oarchive(archive::no_header), - implementation_archive(ar) - { - } - + : archive::detail::common_oarchive(archive::no_header), + implementation_archive(ar) + { + } + #ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS public: #else @@ -50,12 +49,12 @@ protected: { archive::save(* this->This(), t); } - + #define BOOST_ARCHIVE_FORWARD_IMPLEMENTATION(T) \ void save_override(T const & t , int) \ - { \ - implementation_archive << t; \ - } + { \ + implementation_archive << t; \ + } BOOST_ARCHIVE_FORWARD_IMPLEMENTATION(archive::class_id_optional_type) BOOST_ARCHIVE_FORWARD_IMPLEMENTATION(archive::version_type) diff --git a/include/boost/mpi/detail/ignore_skeleton_oarchive.hpp b/include/boost/mpi/detail/ignore_skeleton_oarchive.hpp index a423ecb..d945375 100644 --- a/include/boost/mpi/detail/ignore_skeleton_oarchive.hpp +++ b/include/boost/mpi/detail/ignore_skeleton_oarchive.hpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include #include @@ -21,15 +21,14 @@ namespace boost { namespace mpi { namespace detail { template class ignore_skeleton_oarchive - : public archive::array::oarchive + : public archive::detail::common_oarchive { public: - ignore_skeleton_oarchive() - : archive::array::oarchive(archive::no_header) - { - } - + : archive::detail::common_oarchive(archive::no_header) + { + } + #ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS public: #else @@ -46,10 +45,10 @@ protected: { archive::save(* this->This(), t); } - + #define BOOST_ARCHIVE_IGNORE_IMPLEMENTATION(T) \ void save_override(T const & , int) \ - {} + {} BOOST_ARCHIVE_IGNORE_IMPLEMENTATION(archive::class_id_optional_type) BOOST_ARCHIVE_IGNORE_IMPLEMENTATION(archive::version_type) diff --git a/include/boost/mpi/detail/mpi_datatype_cache.hpp b/include/boost/mpi/detail/mpi_datatype_cache.hpp index 766b460..4564849 100644 --- a/include/boost/mpi/detail/mpi_datatype_cache.hpp +++ b/include/boost/mpi/detail/mpi_datatype_cache.hpp @@ -69,12 +69,12 @@ public: // check whether the type already exists std::type_info const* t = &typeid(T); - MPI_Datatype datatype = get(t); - if (datatype == MPI_DATATYPE_NULL) { + MPI_Datatype datatype = get(t); + if (datatype == MPI_DATATYPE_NULL) { // need to create a type mpi_datatype_oarchive ar(x); - datatype = ar.get_mpi_datatype(); - set(t, datatype); + datatype = ar.get_mpi_datatype(); + set(t, datatype); } return datatype; diff --git a/include/boost/mpi/detail/mpi_datatype_oarchive.hpp b/include/boost/mpi/detail/mpi_datatype_oarchive.hpp index 7580325..8aff6b8 100644 --- a/include/boost/mpi/detail/mpi_datatype_oarchive.hpp +++ b/include/boost/mpi/detail/mpi_datatype_oarchive.hpp @@ -43,5 +43,6 @@ public: // required by export BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::detail::mpi_datatype_oarchive) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::detail::ignore_skeleton_oarchive) +BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::mpi::detail::mpi_datatype_oarchive) #endif // BOOST_MPI_DETAIL_MPI_DATATYPE_OARCHIVE_HPP diff --git a/include/boost/mpi/detail/packed_iprimitive.hpp b/include/boost/mpi/detail/packed_iprimitive.hpp index 54881e6..03d1c80 100644 --- a/include/boost/mpi/detail/packed_iprimitive.hpp +++ b/include/boost/mpi/detail/packed_iprimitive.hpp @@ -65,20 +65,19 @@ public: // fast saving of arrays of fundamental types template - void load_array(serialization::array & x, unsigned int /* file_version */) + void load_array(serialization::array const& x, unsigned int /* file_version */) { if (x.count()) load_impl(x.address(), get_mpi_datatype(*x.address()), x.count()); } - typedef is_mpi_datatype use_array_optimization; + template + void load(serialization::array const& x) + { + load_array(x,0u); + } -#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS - friend class archive::load_access; -protected: -#else -public: -#endif + typedef is_mpi_datatype use_array_optimization; // default saving of primitives. template diff --git a/include/boost/mpi/detail/text_skeleton_oarchive.hpp b/include/boost/mpi/detail/text_skeleton_oarchive.hpp index 460d697..087619b 100644 --- a/include/boost/mpi/detail/text_skeleton_oarchive.hpp +++ b/include/boost/mpi/detail/text_skeleton_oarchive.hpp @@ -13,7 +13,6 @@ #include #include #include -#include namespace boost { namespace mpi { @@ -25,9 +24,9 @@ class text_skeleton_oarchive { public: text_skeleton_oarchive(std::ostream & s, unsigned int flags = 0) - : detail::forward_skeleton_oarchive(skeleton_archive_) + : detail::forward_skeleton_oarchive(skeleton_archive_) , skeleton_archive_(s,flags) - {} + {} private: boost::archive::text_oarchive skeleton_archive_; diff --git a/include/boost/mpi/packed_iarchive.hpp b/include/boost/mpi/packed_iarchive.hpp index 3f2e85d..1500bb0 100644 --- a/include/boost/mpi/packed_iarchive.hpp +++ b/include/boost/mpi/packed_iarchive.hpp @@ -23,10 +23,17 @@ #include #include #include +#include #include namespace boost { namespace mpi { +#ifdef BOOST_MPI_HOMOGENEOUS + typedef binary_buffer_iprimitive iprimitive; +#else + typedef packed_iprimitive iprimitive; +#endif + /** @brief An archive that packs binary data into an MPI buffer. * * The @c packed_iarchive class is an Archiver (as in the @@ -36,7 +43,7 @@ namespace boost { namespace mpi { * implementation to perform serialization. */ class BOOST_MPI_DECL packed_iarchive - : public packed_iprimitive + : public iprimitive , public archive::basic_binary_iarchive , public archive::detail::shared_ptr_helper { @@ -59,7 +66,7 @@ public: * deserialization will begin. */ packed_iarchive(MPI_Comm const & comm, buffer_type & b, unsigned int flags = boost::archive::no_header, int position = 0) - : packed_iprimitive(b,comm,position), + : iprimitive(b,comm,position), archive::basic_binary_iarchive(flags) {} @@ -70,16 +77,44 @@ public: * @param comm The communicator over which this archive will be * sent. * + * @param s The size of the buffer to be received. + * * @param flags Control the serialization of the data types. Refer * to the Boost.Serialization documentation before changing the * default flags. */ packed_iarchive - ( MPI_Comm const & comm , unsigned int flags = boost::archive::no_header) - : packed_iprimitive(internal_buffer_,comm), - archive::basic_binary_iarchive(flags) + ( MPI_Comm const & comm , std::size_t s=0, + unsigned int flags = boost::archive::no_header) + : iprimitive(internal_buffer_,comm) + , archive::basic_binary_iarchive(flags) + , internal_buffer_(s) {} + // Load everything else in the usual way, forwarding on to the Base class + template + void load_override(T& x, int version, mpl::false_) + { + archive::basic_binary_iarchive::load_override(x,version); + } + + // Load it directly using the primnivites + template + void load_override(T& x, int version, mpl::true_) + { + iprimitive::load(x); + } + + // Load all supported datatypes directly + template + void load_override(T& x, int version) + { + typedef typename mpl::apply1::type + >::type use_optimized; + load_override(x, version, use_optimized()); + } + private: /// An internal buffer to be used when the user does not supply his /// own buffer. @@ -90,5 +125,6 @@ private: BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION(boost::mpi::packed_iarchive) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::packed_iarchive) +BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::mpi::packed_iarchive) #endif // BOOST_MPI_PACKED_IARCHIVE_HPP diff --git a/include/boost/mpi/packed_oarchive.hpp b/include/boost/mpi/packed_oarchive.hpp index 45c0342..b5a97f5 100644 --- a/include/boost/mpi/packed_oarchive.hpp +++ b/include/boost/mpi/packed_oarchive.hpp @@ -22,9 +22,16 @@ #include #include #include +#include namespace boost { namespace mpi { +#ifdef BOOST_MPI_HOMOGENEOUS + typedef binary_buffer_oprimitive oprimitive; +#else + typedef packed_oprimitive oprimitive; +#endif + /** @brief An archive that unpacks binary data from an MPI buffer. * * The @c packed_oarchive class is an Archiver (as in the @@ -33,8 +40,9 @@ namespace boost { namespace mpi { * type and will use the @c MPI_Unpack function of the underlying MPI * implementation to perform deserialization. */ + class BOOST_MPI_DECL packed_oarchive - : public packed_oprimitive, + : public oprimitive, public archive::basic_binary_oarchive { public: @@ -53,7 +61,7 @@ public: * default flags. */ packed_oarchive( MPI_Comm const & comm, buffer_type & b, unsigned int flags = boost::archive::no_header) - : packed_oprimitive(b,comm), + : oprimitive(b,comm), archive::basic_binary_oarchive(flags) {} @@ -69,10 +77,31 @@ public: * default flags. */ packed_oarchive ( MPI_Comm const & comm, unsigned int flags = boost::archive::no_header) - : packed_oprimitive(internal_buffer_,comm), + : oprimitive(internal_buffer_,comm), archive::basic_binary_oarchive(flags) {} + // Save everything else in the usual way, forwarding on to the Base class + template + void save_override(T const& x, int version, mpl::false_) + { + archive::basic_binary_oarchive::save_override(x,version); + } + + // Save it directly using the primnivites + template + void save_override(T const& x, int version, mpl::true_) + { + oprimitive::save(x); + } + + // Save all supported datatypes directly + template + void save_override(T const& x, int version) + { + typedef typename mpl::apply1::type use_optimized; + save_override(x, version, use_optimized()); + } private: /// An internal buffer to be used when the user does not supply his @@ -84,6 +113,8 @@ private: // required by export BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::packed_oarchive) +BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::mpi::packed_oarchive) + #endif // BOOST_MPI_PACKED_OARCHIVE_HPP diff --git a/include/boost/mpi/skeleton_and_content.hpp b/include/boost/mpi/skeleton_and_content.hpp index 5d9c888..87b1f42 100644 --- a/include/boost/mpi/skeleton_and_content.hpp +++ b/include/boost/mpi/skeleton_and_content.hpp @@ -385,4 +385,7 @@ BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::packed_skeleton_iarchive) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::detail::type1) BOOST_SERIALIZATION_REGISTER_ARCHIVE(boost::mpi::detail::type2) +BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::mpi::packed_skeleton_oarchive) +BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION(boost::mpi::packed_skeleton_iarchive) + #endif // BOOST_MPI_SKELETON_AND_CONTENT_HPP diff --git a/include/boost/mpi/status.hpp b/include/boost/mpi/status.hpp index 734f075..d444faa 100644 --- a/include/boost/mpi/status.hpp +++ b/include/boost/mpi/status.hpp @@ -34,6 +34,8 @@ class BOOST_MPI_DECL status { public: status() : m_count(-1) { } + + status(MPI_Status const& s) : m_status(s), m_count(-1) {} /** * Retrieve the source of the message. diff --git a/src/content_oarchive.cpp b/src/content_oarchive.cpp index 357c02e..6d9f084 100644 --- a/src/content_oarchive.cpp +++ b/src/content_oarchive.cpp @@ -14,5 +14,6 @@ namespace boost { namespace archive { namespace detail { // explicitly instantiate all required template functions template class archive_pointer_oserializer ; - +template class archive_pointer_oserializer >; +template class archive_pointer_oserializer >; } } } diff --git a/src/mpi_datatype_cache.cpp b/src/mpi_datatype_cache.cpp index 51ec98c..ab52b64 100644 --- a/src/mpi_datatype_cache.cpp +++ b/src/mpi_datatype_cache.cpp @@ -13,16 +13,16 @@ namespace boost { namespace mpi { namespace detail { typedef std::map - stored_map_type; + stored_map_type; struct mpi_datatype_map::implementation { - stored_map_type map; + stored_map_type map; }; mpi_datatype_map::mpi_datatype_map() { - impl = new implementation(); + impl = new implementation(); } mpi_datatype_map::~mpi_datatype_map() @@ -30,32 +30,31 @@ namespace boost { namespace mpi { namespace detail { // do not free after call to MPI_FInalize int finalized=0; BOOST_MPI_CHECK_RESULT(MPI_Finalized,(&finalized)); - if (!finalized) { - // ignore errors in the destructor - for (stored_map_type::iterator it=impl->map.begin(); it != impl->map.end(); ++it) { - MPI_Type_free(&(it->second)); - } - } - delete impl; + if (!finalized) { + // ignore errors in the destructor + for (stored_map_type::iterator it=impl->map.begin(); it != impl->map.end(); ++it) + MPI_Type_free(&(it->second)); + } + delete impl; } MPI_Datatype mpi_datatype_map::get(const std::type_info* t) { - stored_map_type::iterator pos = impl->map.find(t); - if (pos != impl->map.end()) - return pos->second; - else - return MPI_DATATYPE_NULL; + stored_map_type::iterator pos = impl->map.find(t); + if (pos != impl->map.end()) + return pos->second; + else + return MPI_DATATYPE_NULL; } void mpi_datatype_map::set(const std::type_info* t, MPI_Datatype datatype) { - impl->map[t] = datatype; + impl->map[t] = datatype; } mpi_datatype_map& mpi_datatype_cache() { - static mpi_datatype_map cache; - return cache; + static mpi_datatype_map cache; + return cache; } } } } diff --git a/src/packed_skeleton_iarchive.cpp b/src/packed_skeleton_iarchive.cpp index a088783..2047103 100644 --- a/src/packed_skeleton_iarchive.cpp +++ b/src/packed_skeleton_iarchive.cpp @@ -8,7 +8,6 @@ #define BOOST_ARCHIVE_SOURCE #include -#include #include #include @@ -21,6 +20,9 @@ namespace boost { namespace archive { template class basic_binary_iarchive ; template class detail::archive_pointer_iserializer ; +template class detail::archive_pointer_iserializer< + mpi::detail::forward_skeleton_iarchive< + boost::mpi::packed_skeleton_iarchive, boost::mpi::packed_iarchive> > ; //template class binary_iarchive_impl ; } } // end namespace boost::archive diff --git a/src/packed_skeleton_oarchive.cpp b/src/packed_skeleton_oarchive.cpp index 9db105b..bb4601a 100644 --- a/src/packed_skeleton_oarchive.cpp +++ b/src/packed_skeleton_oarchive.cpp @@ -18,6 +18,9 @@ namespace boost { namespace archive { // explicitly instantiate all required templates template class detail::archive_pointer_oserializer ; +template class detail::archive_pointer_oserializer< + mpi::detail::forward_skeleton_oarchive< + boost::mpi::packed_skeleton_oarchive, boost::mpi::packed_oarchive> > ; template class basic_binary_oarchive ; //template class binary_oarchive_impl ; diff --git a/src/point_to_point.cpp b/src/point_to_point.cpp index 78351c4..dd0d9bc 100644 --- a/src/point_to_point.cpp +++ b/src/point_to_point.cpp @@ -32,7 +32,7 @@ packed_archive_send(MPI_Comm comm, int dest, int tag, BOOST_MPI_CHECK_RESULT(MPI_Send, (const_cast(size), 1, get_mpi_datatype(ar.size()), - dest, tag, comm)); + dest, tag, comm)); BOOST_MPI_CHECK_RESULT(MPI_Send, (const_cast(ar.address()), ar.size(), MPI_PACKED, diff --git a/src/python/documentation.cpp b/src/python/documentation.cpp index c1fd89a..ef28fb3 100644 --- a/src/python/documentation.cpp +++ b/src/python/documentation.cpp @@ -132,10 +132,10 @@ const char* module_docstring = "\n" " Once you have registered your C++ data structures, you can extract\n" " the skeleton for an instance of that data structure with skeleton().\n" - " The resulting skeleton_proxy can be transmitted via the normal send\n" + " The resulting SkeletonProxy can be transmitted via the normal send\n" " routine, e.g.,\n\n" " mpi.world.send(1, 0, skeleton(my_data_structure))\n\n" - " skeleton_proxy objects can be received on the other end via recv(),\n" + " SkeletonProxy objects can be received on the other end via recv(),\n" " which stores a newly-created instance of your data structure with the\n" " same `shape' as the sender in its `object' attribute:\n\n" " shape = mpi.world.recv(0, 0)\n" @@ -211,6 +211,86 @@ const char* environment_initialized_docstring = const char* environment_finalized_docstring = "Determine if the MPI environment has already been finalized.\n"; +/*********************************************************** + * nonblocking documentation * + ***********************************************************/ +const char* request_list_init_docstring= + "Without arguments, constructs an empty RequestList.\n" + "With one argument `iterable', copies request objects from this\n" + "iterable to the new RequestList.\n"; + +const char* nonblocking_wait_any_docstring = + "Waits until any of the given requests has been completed. It provides\n" + "functionality equivalent to MPI_Waitany.\n" + "\n" + "requests must be a RequestList instance.\n" + "\n" + "Returns a triple (value, status, index) consisting of received value\n" + "(or None), the Status object for the completed request, and its index\n" + "in the RequestList.\n"; + +const char* nonblocking_test_any_docstring = + "Tests if any of the given requests have been completed, but does not wait\n" + "for completion. It provides functionality equivalent to MPI_Testany.\n" + "\n" + "requests must be a RequestList instance.\n" + "\n" + "Returns a triple (value, status, index) like wait_any or None if no request\n" + "is complete.\n"; + +const char* nonblocking_wait_all_docstring = + "Waits until all of the given requests have been completed. It provides\n" + "functionality equivalent to MPI_Waitall.\n" + "\n" + "requests must be a RequestList instance.\n" + "\n" + "If the second parameter `callable' is provided, it is called with each\n" + "completed request's received value (or None) and it s Status object as\n" + "its arguments. The calls occur in the order given by the `requests' list.\n"; + +const char* nonblocking_test_all_docstring = + "Tests if all of the given requests have been completed. It provides\n" + "functionality equivalent to MPI_Testall.\n" + "\n" + "Returns True if all requests have been completed.\n" + "\n" + "requests must be a RequestList instance.\n" + "\n" + "If the second parameter `callable' is provided, it is called with each\n" + "completed request's received value (or None) and it s Status object as\n" + "its arguments. The calls occur in the order given by the `requests' list.\n"; + +const char* nonblocking_wait_some_docstring = + "Waits until at least one of the given requests has completed. It\n" + "then completes all of the requests it can, partitioning the input\n" + "sequence into pending requests followed by completed requests.\n" + "\n" + "This routine provides functionality equivalent to MPI_Waitsome.\n" + "\n" + "Returns the index of the first completed request." + "\n" + "requests must be a RequestList instance.\n" + "\n" + "If the second parameter `callable' is provided, it is called with each\n" + "completed request's received value (or None) and it s Status object as\n" + "its arguments. The calls occur in the order given by the `requests' list.\n"; + +const char* nonblocking_test_some_docstring = + "Tests to see if any of the given requests has completed. It completes\n" + "all of the requests it can, partitioning the input sequence into pending\n" + "requests followed by completed requests. This routine is similar to\n" + "wait_some, but does not wait until any requests have completed.\n" + "\n" + "This routine provides functionality equivalent to MPI_Testsome.\n" + "\n" + "Returns the index of the first completed request." + "\n" + "requests must be a RequestList instance.\n" + "\n" + "If the second parameter `callable' is provided, it is called with each\n" + "completed request's received value (or None) and it s Status object as\n" + "its arguments. The calls occur in the order given by the `requests' list.\n"; + /*********************************************************** * exception documentation * ***********************************************************/ @@ -310,14 +390,14 @@ const char* scatter_docstring = * communicator documentation * ***********************************************************/ const char* communicator_docstring = - "The communicator class abstracts a set of communicating\n" + "The Communicator class abstracts a set of communicating\n" "processes in MPI. All of the processes that belong to a certain\n" "communicator can determine the size of the communicator, their rank\n" "within the communicator, and communicate with any other processes\n" "in the communicator.\n"; const char* communicator_default_constructor_docstring = - "Build a new Boost.MPI communicator for MPI_COMM_WORLD.\n"; + "Build a new Boost.MPI Communicator instance for MPI_COMM_WORLD.\n"; const char* communicator_rank_docstring = "Returns the rank of the process in the communicator, which will be a\n" @@ -335,10 +415,10 @@ const char* communicator_send_docstring = " - For C++ objects registered via register_serialized(), the value\n" " will be serialized and transmitted.\n" "\n" - " - For skeleton_proxy objects, the skeleton of the object will be\n" + " - For SkeletonProxy objects, the skeleton of the object will be\n" " serialized and transmitted.\n" "\n" - " - For content objects, the content will be transmitted directly.\n" + " - For Content objects, the content will be transmitted directly.\n" " This content can be received by a matching recv/irecv call that\n" " provides a suitable `buffer' argument.\n" "\n" @@ -351,12 +431,12 @@ const char* communicator_recv_docstring = "the message can be received from any process. Likewise, if the tag\n" "parameter is not specified, a message with any tag can be received.\n" "If return_status is True, returns a tuple containing the received\n" - "object followed by a status object describing the communication.\n" + "object followed by a Status object describing the communication.\n" "Otherwise, recv() returns just the received object.\n" "\n" "When receiving the content of a data type that has been sent separately\n" "from its skeleton, user code must provide a value for the `buffer'\n" - "argument. This value should be the content object returned from\n" + "argument. This value should be the Content object returned from\n" "get_content().\n"; const char* communicator_isend_docstring = @@ -364,7 +444,7 @@ const char* communicator_isend_docstring = "tag to the process with rank dest. It can be received by the\n" "destination process with a matching recv call. The value will be\n" "transmitted in the same way as with send().\n" - "This routine returns a request object, which can be used to query\n" + "This routine returns a Request object, which can be used to query\n" "when the transmission has completed, wait for its completion, or\n" "cancel the transmission.\n"; @@ -373,15 +453,15 @@ const char* communicator_irecv_docstring = "source with the given tag. If the source parameter is not specified,\n" "the message can be received from any process. Likewise, if the tag\n" "parameter is not specified, a message with any tag can be received.\n" - "This routine returns a request object, which can be used to query\n" + "This routine returns a Request object, which can be used to query\n" "when the transmission has completed, wait for its completion, or\n" "cancel the transmission. The received value be accessible\n" - "through the `value' attribute of the request object once transmission\n" + "through the `value' attribute of the Request object once transmission\n" "has completed.\n" "\n" "As with the recv() routine, when receiving the content of a data type\n" "that has been sent separately from its skeleton, user code must provide\n" - "a value for the `buffer' argument. This value should be the content\n" + "a value for the `buffer' argument. This value should be the Content\n" "object returned from get_content().\n"; const char* communicator_probe_docstring = @@ -389,7 +469,7 @@ const char* communicator_irecv_docstring = "is available to be received. It then returns information about\n" "that message. If source is omitted, a message from any process\n" "will match. If tag is omitted, a message with any tag will match.\n" - "The actual source and tag can be retrieved from the returned status\n" + "The actual source and tag can be retrieved from the returned Status\n" "object. To check if a message is available without blocking, use\n" "iprobe.\n"; @@ -399,7 +479,7 @@ const char* communicator_iprobe_docstring = "message; otherwise, it returns None. If source is omitted, a message\n" "from any process will match. If tag is omitted, a message with any\n" "tag will match. The actual source and tag can be retrieved from the\n" - "returned status object. To wait for a message to become available, use\n" + "returned Status object. To wait for a message to become available, use\n" "probe.\n"; const char* communicator_barrier_docstring = @@ -418,8 +498,8 @@ const char* communicator_split_docstring = "the ordering of processes with the same color in the resulting\n" "communicator. If omitted, the key will default to the rank of\n" "the process in the current communicator.\n\n" - "Returns a new communicator containing all of the processes in\n" - "this communicator that have the same color.\n"; + "Returns a new Communicator instance containing all of the \n" + "processes in this communicator that have the same color.\n"; const char* communicator_abort_docstring = "Makes a \"best attempt\" to abort all of the tasks in the group of\n" @@ -435,36 +515,46 @@ const char* communicator_abort_docstring = * request documentation * ***********************************************************/ const char* request_docstring = - "The request class contains information about a non-blocking send\n" + "The Request class contains information about a non-blocking send\n" "or receive and will be returned from isend or irecv, respectively.\n" - "When a request object represents a completed irecv, the `value' \n" + "When a Request object represents a completed irecv, the `value' \n" "attribute will contain the received value.\n"; +const char* request_with_value_docstring = + "This class is an implementation detail. Any call that accepts a\n" + "Request also accepts a RequestWithValue, and vice versa.\n"; + const char* request_wait_docstring = "Wait until the communication associated with this request has\n" "completed. For a request that is associated with an isend(), returns\n" - "a status object describing the communication. For an irecv()\n" + "a Status object describing the communication. For an irecv()\n" "operation, returns the received value by default. However, when\n" - "return_status=True, a (value, status) pair is returned by a.\n" + "return_status=True, a (value, status) pair is returned by a\n" "completed irecv request.\n"; const char* request_test_docstring = "Determine whether the communication associated with this request\n" - "has completed successfully. If so, returns the status object\n" + "has completed successfully. If so, returns the Status object\n" "describing the communication (for an isend request) or a tuple\n" - "containing the received value and a status object (for an irecv\n" - "request). Note that once test() returns a status object, the\n" + "containing the received value and a Status object (for an irecv\n" + "request). Note that once test() returns a Status object, the\n" "request has completed and wait() should not be called.\n"; const char* request_cancel_docstring = "Cancel a pending communication, assuming it has not already been\n" "completed.\n"; +const char* request_value_docstring = + "If this request originated in an irecv(), this property makes the" + "sent value accessible once the request completes.\n" + "\n" + "If no value is available, ValueError is raised.\n"; + /*********************************************************** * skeleton/content documentation * ***********************************************************/ const char* object_without_skeleton_docstring = - "The object_without_skeleton class is an exception class used only\n" + "The ObjectWithoutSkeleton class is an exception class used only\n" "when the skeleton() or get_content() function is called with an\n" "object that is not supported by the skeleton/content mechanism.\n" "All C++ types for which skeletons and content can be transmitted\n" @@ -475,13 +565,13 @@ const char* object_without_skeleton_object_docstring = "The object on which skeleton() or get_content() was invoked.\n"; const char* skeleton_proxy_docstring = - "The skeleton_proxy class is used to represent the skeleton of an\n" - "object. The skeleton_proxy can be used as the value parameter of\n" + "The SkeletonProxy class is used to represent the skeleton of an\n" + "object. The SkeletonProxy can be used as the value parameter of\n" "send() or isend() operations, but instead of transmitting the\n" "entire object, only its skeleton (\"shape\") will be sent, without\n" "the actual data. Its content can then be transmitted, separately.\n" "\n" - "User code cannot generate skeleton_proxy instances directly. To\n" + "User code cannot generate SkeletonProxy instances directly. To\n" "refer to the skeleton of an object, use skeleton(object). Skeletons\n" "can also be received with the recv() and irecv() methods.\n" "\n" @@ -503,7 +593,7 @@ const char* content_docstring = "skeleton/content mechanism.\n"; const char* skeleton_docstring = - "The skeleton function retrieves the skeleton_proxy for its object\n" + "The skeleton function retrieves the SkeletonProxy for its object\n" "parameter, allowing the transmission of the skeleton (or \"shape\")\n" "of the object separately from its data. The skeleton/content mechanism\n" "is useful when a large data structure remains structurally the same\n" @@ -534,7 +624,7 @@ const char* get_content_docstring = * status documentation * ***********************************************************/ const char* status_docstring = - "The status class stores information about a given message, including\n" + "The Status class stores information about a given message, including\n" "its source, tag, and whether the message transmission was cancelled\n" "or resulted in an error.\n"; @@ -554,7 +644,7 @@ const char* status_cancelled_docstring = * timer documentation * ***********************************************************/ const char* timer_docstring = - "The timer class is a simple wrapper around the MPI timing facilities.\n"; + "The Timer class is a simple wrapper around the MPI timing facilities.\n"; const char* timer_default_constructor_docstring = "Initializes the timer. After this call, elapsed == 0.\n"; diff --git a/src/python/module.cpp b/src/python/module.cpp index f365b7e..ec36968 100644 --- a/src/python/module.cpp +++ b/src/python/module.cpp @@ -27,6 +27,7 @@ extern void export_datatypes(); extern void export_request(); extern void export_status(); extern void export_timer(); +extern void export_nonblocking(); extern const char* module_docstring; @@ -35,7 +36,7 @@ BOOST_PYTHON_MODULE(mpi) // Setup module documentation scope().attr("__doc__") = module_docstring; scope().attr("__author__") = "Douglas Gregor "; - scope().attr("__date__") = "$LastChangedDate: 2006-07-16 15:25:47 -0400 (Sun, 16 Jul 2006) $"; + scope().attr("__date__") = "$LastChangedDate$"; scope().attr("__version__") = "$Revision$"; scope().attr("__copyright__") = "Copyright (C) 2006 Douglas Gregor"; scope().attr("__license__") = "http://www.boost.org/LICENSE_1_0.txt"; @@ -48,6 +49,7 @@ BOOST_PYTHON_MODULE(mpi) export_request(); export_status(); export_timer(); + export_nonblocking(); } } } } // end namespace boost::mpi::python diff --git a/src/python/py_communicator.cpp b/src/python/py_communicator.cpp index 4b7ce50..6e53f56 100644 --- a/src/python/py_communicator.cpp +++ b/src/python/py_communicator.cpp @@ -14,6 +14,7 @@ #include #include #include +#include "request_with_value.hpp" using namespace boost::python; using namespace boost::mpi; @@ -49,14 +50,12 @@ communicator_recv(const communicator& comm, int source, int tag, return result; } -object +request_with_value communicator_irecv(const communicator& comm, int source, int tag) { - using boost::python::make_tuple; - - object result; - object req(comm.irecv(source, tag, result)); - req.attr("value") = result; + boost::shared_ptr result(new object()); + request_with_value req(comm.irecv(source, tag, *result)); + req.m_internal_value = result; return req; } @@ -76,7 +75,7 @@ void export_communicator() using boost::python::arg; using boost::python::object; - class_ comm("communicator", communicator_docstring); + class_ comm("Communicator", communicator_docstring); comm .def(init<>()) .add_property("rank", &communicator::rank, communicator_rank_docstring) diff --git a/src/python/py_exception.cpp b/src/python/py_exception.cpp index e19c0eb..fee48c4 100644 --- a/src/python/py_exception.cpp +++ b/src/python/py_exception.cpp @@ -30,9 +30,8 @@ extern const char* exception_result_code_docstring; str exception_str(const exception& e) { - return str("MPI routine `" + std::string(e.routine()) + - "' returned error code " + - lexical_cast(e.result_code())); + return str(std::string(e.what()) + + " (code " + lexical_cast(e.result_code())+")"); } void export_exception() @@ -42,10 +41,10 @@ void export_exception() object type = class_ - ("exception", exception_docstring, no_init) + ("Exception", exception_docstring, no_init) .add_property("what", &exception::what, exception_what_docstring) .add_property("routine", &exception::what, exception_routine_docstring) - .add_property("result_code", &exception::what, + .add_property("result_code", &exception::result_code, exception_result_code_docstring) .def("__str__", &exception_str) ; diff --git a/src/python/py_nonblocking.cpp b/src/python/py_nonblocking.cpp new file mode 100644 index 0000000..22f289f --- /dev/null +++ b/src/python/py_nonblocking.cpp @@ -0,0 +1,255 @@ +// (C) Copyright 2007 +// Douglas Gregor +// Andreas Kloeckner + +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Douglas Gregor, Andreas Kloeckner + +/** @file py_nonblocking.cpp + * + * This file reflects the Boost.MPI nonblocking operations into Python + * functions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "request_with_value.hpp" + +using namespace std; +using namespace boost::python; +using namespace boost::mpi; + + + + +namespace +{ + template + class py_call_output_iterator : + public boost::output_iterator_helper< + py_call_output_iterator > + { + private: + object m_callable; + RequestIterator m_request_iterator; + + public: + explicit py_call_output_iterator(object callable, + const RequestIterator &req_it) + : m_callable(callable), m_request_iterator(req_it) + { } + + py_call_output_iterator &operator=(ValueType const &v) + { + m_callable((m_request_iterator++)->get_value_or_none(), v); + return *this; + } + }; + + + + + typedef std::vector request_list; + typedef py_call_output_iterator + status_value_iterator; + + + + + std::auto_ptr make_request_list_from_py_list(object iterable) + { + std::auto_ptr result(new request_list); + std::copy( + stl_input_iterator(iterable), + stl_input_iterator(), + back_inserter(*result)); + return result; + } + + + + + class request_list_indexing_suite : + public vector_indexing_suite + { + public: + // FIXME: requests are not comparable, thus __contains__ makes no sense. + // Unfortunately, indexing_suites insist on having __contains__ available. + // Just make it error out for now. + + static bool + contains(request_list& container, request const& key) + { + PyErr_SetString(PyExc_NotImplementedError, "mpi requests are not comparable"); + throw error_already_set(); + } + }; + + + + + void check_request_list_not_empty(const request_list &requests) + { + if (requests.size() == 0) + { + PyErr_SetString(PyExc_ValueError, "cannot wait on an empty request vector"); + throw error_already_set(); + } + + } + + + + + + object wrap_wait_any(request_list &requests) + { + check_request_list_not_empty(requests); + + pair result = + wait_any(requests.begin(), requests.end()); + + return make_tuple( + result.second->get_value_or_none(), + result.first, + distance(requests.begin(), result.second)); + } + + + + + object wrap_test_any(request_list &requests) + { + check_request_list_not_empty(requests); + ::boost::optional > result = + test_any(requests.begin(), requests.end()); + + if (result) + return make_tuple( + result->second->get_value_or_none(), + result->first, + distance(requests.begin(), result->second)); + else + return object(); + } + + + + + + void wrap_wait_all(request_list &requests, object py_callable) + { + check_request_list_not_empty(requests); + if (py_callable != object()) + wait_all(requests.begin(), requests.end(), + status_value_iterator(py_callable, requests.begin())); + else + wait_all(requests.begin(), requests.end()); + } + + + + + bool wrap_test_all(request_list &requests, object py_callable) + { + check_request_list_not_empty(requests); + if (py_callable != object()) + return test_all(requests.begin(), requests.end(), + status_value_iterator(py_callable, requests.begin())); + else + return test_all(requests.begin(), requests.end()); + } + + + + + int wrap_wait_some(request_list &requests, object py_callable) + { + check_request_list_not_empty(requests); + request_list::iterator first_completed; + if (py_callable != object()) + first_completed = wait_some(requests.begin(), requests.end(), + status_value_iterator(py_callable, requests.begin())).second; + else + first_completed = wait_some(requests.begin(), requests.end()); + + return distance(requests.begin(), first_completed); + } + + + + + int wrap_test_some(request_list &requests, object py_callable) + { + check_request_list_not_empty(requests); + request_list::iterator first_completed; + if (py_callable != object()) + first_completed = test_some(requests.begin(), requests.end(), + status_value_iterator(py_callable, requests.begin())).second; + else + first_completed = test_some(requests.begin(), requests.end()); + + return distance(requests.begin(), first_completed); + } +} + + + + +namespace boost { namespace mpi { namespace python { + +extern const char* request_list_init_docstring; +extern const char* request_list_append_docstring; + +extern const char* nonblocking_wait_any_docstring; +extern const char* nonblocking_test_any_docstring; +extern const char* nonblocking_wait_all_docstring; +extern const char* nonblocking_test_all_docstring; +extern const char* nonblocking_wait_some_docstring; +extern const char* nonblocking_test_some_docstring; + +void export_nonblocking() +{ + using boost::python::arg; + + { + typedef request_list cl; + class_("RequestList", "A list of Request objects.") + .def("__init__", make_constructor(make_request_list_from_py_list), + /*arg("iterable"),*/ request_list_init_docstring) + .def(request_list_indexing_suite()) + ; + } + + def("wait_any", wrap_wait_any, + (arg("requests")), + nonblocking_wait_any_docstring); + def("test_any", wrap_test_any, + (arg("requests")), + nonblocking_test_any_docstring); + + def("wait_all", wrap_wait_all, + (arg("requests"), arg("callable") = object()), + nonblocking_wait_all_docstring); + def("test_all", wrap_test_all, + (arg("requests"), arg("callable") = object()), + nonblocking_test_all_docstring); + + def("wait_some", wrap_wait_some, + (arg("requests"), arg("callable") = object()), + nonblocking_wait_some_docstring); + def("test_some", wrap_test_some, + (arg("requests"), arg("callable") = object()), + nonblocking_test_some_docstring); +} + +} } } diff --git a/src/python/py_request.cpp b/src/python/py_request.cpp index 2746265..53aa4de 100644 --- a/src/python/py_request.cpp +++ b/src/python/py_request.cpp @@ -13,52 +13,90 @@ */ #include #include +#include "request_with_value.hpp" using namespace boost::python; using namespace boost::mpi; -namespace boost { namespace mpi { namespace python { - -extern const char* request_docstring; -extern const char* request_wait_docstring; -extern const char* request_test_docstring; -extern const char* request_cancel_docstring; - -object request_wait(object req_obj) +const object python::request_with_value::get_value() const { - request& req = extract(req_obj)(); - status stat = req.wait(); - if (PyObject_HasAttrString(req_obj.ptr(), "value")) - return boost::python::make_tuple(stat, req_obj.attr("value")); + if (m_internal_value.get()) + return *m_internal_value; + else if (m_external_value) + return *m_external_value; else - return object(stat); + { + PyErr_SetString(PyExc_ValueError, "request value not available"); + throw boost::python::error_already_set(); + } } -object request_test(object req_obj) +const object python::request_with_value::get_value_or_none() const { - request& req = extract(req_obj)(); - - if (optional stat = req.test()) - { - if (PyObject_HasAttrString(req_obj.ptr(), "value")) - return boost::python::make_tuple(stat, req_obj.attr("value")); - else - return object(stat); - } + if (m_internal_value.get()) + return *m_internal_value; + else if (m_external_value) + return *m_external_value; else return object(); } +const object python::request_with_value::wrap_wait() +{ + status stat = request::wait(); + if (m_internal_value.get() || m_external_value) + return boost::python::make_tuple(get_value(), stat); + else + return object(stat); +} + +const object python::request_with_value::wrap_test() +{ + ::boost::optional stat = request::test(); + if (stat) + { + if (m_internal_value.get() || m_external_value) + return boost::python::make_tuple(get_value(), *stat); + else + return object(*stat); + } + else + return object(); +} + + +namespace boost { namespace mpi { namespace python { + +extern const char* request_docstring; +extern const char* request_with_value_docstring; +extern const char* request_wait_docstring; +extern const char* request_test_docstring; +extern const char* request_cancel_docstring; +extern const char* request_value_docstring; + void export_request() { using boost::python::arg; using boost::python::object; - class_("request", request_docstring, no_init) - .def("wait", &request_wait, request_wait_docstring) - .def("test", &request_test, request_test_docstring) - .def("cancel", &request::cancel, request_cancel_docstring) - ; + { + typedef request cl; + class_("Request", request_docstring, no_init) + .def("wait", &cl::wait, request_wait_docstring) + .def("test", &cl::test, request_test_docstring) + .def("cancel", &cl::cancel, request_cancel_docstring) + ; + } + { + typedef request_with_value cl; + class_ >( + "RequestWithValue", request_with_value_docstring, no_init) + .def("wait", &cl::wrap_wait, request_wait_docstring) + .def("test", &cl::wrap_test, request_test_docstring) + ; + } + + implicitly_convertible(); } } } } // end namespace boost::mpi::python diff --git a/src/python/py_timer.cpp b/src/python/py_timer.cpp index d33f694..88b1b40 100644 --- a/src/python/py_timer.cpp +++ b/src/python/py_timer.cpp @@ -32,7 +32,7 @@ void export_timer() using boost::python::arg; using boost::python::object; - class_("timer", timer_docstring) + class_("Timer", timer_docstring) .def(init<>()) .def("restart", &timer::restart, timer_restart_docstring) .add_property("elapsed", &timer::elapsed, timer_elapsed_docstring) diff --git a/src/python/request_with_value.hpp b/src/python/request_with_value.hpp new file mode 100644 index 0000000..04d0c53 --- /dev/null +++ b/src/python/request_with_value.hpp @@ -0,0 +1,71 @@ +// (C) Copyright 2006 +// Douglas Gregor +// Andreas Kloeckner + +// Use, modification and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +// Authors: Douglas Gregor, Andreas Kloeckner + +#ifndef BOOST_MPI_PYTHON_REQUEST_WITH_VALUE_HPP +#define BOOST_MPI_PYTHON_REQUEST_WITH_VALUE_HPP + +#include +#include + +namespace boost { namespace mpi { namespace python { + + /** This wrapper adds a @c boost::python::object value to the @c + * boost::mpi::request structure, for the benefit of @c irecv() requests. + * + * In order to be able to return the value of his requests to the user, we + * need a handle that we can update to contain the transmitted value once the + * request completes. Since we're passing the address on to irecv to fill at + * any time in the future, this address may not change over time. + * + * There are two possible cases: + * - plain irecv() + * - skeleton-content irecv() + * + * In the first case, we need to own the storage from this object, the + * m_internal_value is used for this. In the second case, the updated + * python::object is part of a boost::mpi::python::content object: the + * m_external_value field handles this case. Furthermore, in the latter case, + * we now have a lifetime dependency on that content object; this can be + * handled with the BPL's with_custodian_and_ward facility. + * + * Since requests and request_with_value are supposed to be copyconstructible, + * we can't put the handle immediately inside this instance. Moreover, since + * we need to be able to put request_with_value inside request_vectors, any + * values we own must be held in a shared_ptr instance. + */ + + class request_with_value : public request + { + private: + boost::shared_ptr m_internal_value; + boost::python::object *m_external_value; + + public: + request_with_value() + : m_external_value(0) + { } + request_with_value(const request &req) + : request(req), m_external_value(0) + { } + + const boost::python::object get_value() const; + const boost::python::object get_value_or_none() const; + + const boost::python::object wrap_wait(); + const boost::python::object wrap_test(); + + friend request_with_value communicator_irecv(const communicator &, int, int); + friend request_with_value communicator_irecv_content( + const communicator&, int, int, content&); + }; + +} } } + +#endif diff --git a/src/python/skeleton_and_content.cpp b/src/python/skeleton_and_content.cpp index 45a39d1..d5376c1 100644 --- a/src/python/skeleton_and_content.cpp +++ b/src/python/skeleton_and_content.cpp @@ -16,6 +16,7 @@ #include #include #include "utility.hpp" +#include "request_with_value.hpp" using namespace boost::python; using namespace boost::mpi; @@ -114,14 +115,12 @@ communicator_recv_content(const communicator& comm, int source, int tag, /// Receive the content of a Python object. The request object's value /// attribute will reference the object whose content is being /// received, not the content wrapper. -object +request_with_value communicator_irecv_content(const communicator& comm, int source, int tag, - const content& c) + content& c) { - using boost::python::make_tuple; - - object req(comm.irecv(source, tag, c.base())); - req.attr("value") = c.object; + request_with_value req(comm.irecv(source, tag, c.base())); + req.m_external_value = &c.object; return req; } @@ -140,7 +139,7 @@ void export_skeleton_and_content(class_& comm) // Expose the object_without_skeleton exception object type = class_ - ("object_without_skeleton", object_without_skeleton_docstring, no_init) + ("ObjectWithoutSkeleton", object_without_skeleton_docstring, no_init) .def_readonly("object", &object_without_skeleton::value, object_without_skeleton_object_docstring) .def("__str__", &object_without_skeleton_str) @@ -150,11 +149,11 @@ void export_skeleton_and_content(class_& comm) // Expose the Python variants of "skeleton_proxy" and "content", and // their generator functions. detail::skeleton_proxy_base_type = - class_("skeleton_proxy", skeleton_proxy_docstring, + class_("SkeletonProxy", skeleton_proxy_docstring, no_init) .def_readonly("object", &skeleton_proxy_base::object, skeleton_proxy_object_docstring); - class_("content", content_docstring, no_init); + class_("Content", content_docstring, no_init); def("skeleton", &skeleton, arg("object"), skeleton_docstring); def("get_content", &get_content, arg("object"), get_content_docstring); @@ -166,7 +165,9 @@ void export_skeleton_and_content(class_& comm) (arg("source") = any_source, arg("tag") = any_tag, arg("buffer"), arg("return_status") = false)) .def("irecv", communicator_irecv_content, - (arg("source") = any_source, arg("tag") = any_tag, arg("buffer"))); + (arg("source") = any_source, arg("tag") = any_tag, arg("buffer")), + with_custodian_and_ward_postcall<0, 4>() + ); } } } } // end namespace boost::mpi::python diff --git a/src/python/status.cpp b/src/python/status.cpp index 51e1d27..a74221a 100644 --- a/src/python/status.cpp +++ b/src/python/status.cpp @@ -30,7 +30,7 @@ void export_status() using boost::python::arg; using boost::python::object; - class_("status", status_docstring, no_init) + class_("Status", status_docstring, no_init) .add_property("source", &status::source, status_source_docstring) .add_property("tag", &status::tag, status_tag_docstring) .add_property("error", &status::error, status_error_docstring) diff --git a/test/broadcast_test.cpp b/test/broadcast_test.cpp index 426efd8..a513a62 100644 --- a/test/broadcast_test.cpp +++ b/test/broadcast_test.cpp @@ -55,7 +55,6 @@ test_skeleton_and_content(const communicator& comm, int root = 0) using boost::mpi::get_content; using boost::make_counting_iterator; using boost::mpi::broadcast; - using boost::mpi::content; using boost::mpi::get_content; typedef std::list::iterator iterator;