diff --git a/Framework/CMakeLists.txt b/Framework/CMakeLists.txt index 3d5d8354bc366fa40c7812c609a4c7feca49c552..6d1f4447c79cf546f8b5f51f7ed52a0141cb74f7 100644 --- a/Framework/CMakeLists.txt +++ b/Framework/CMakeLists.txt @@ -74,6 +74,9 @@ add_subdirectory (Types) include_directories (Kernel/inc) add_subdirectory (Kernel) +include_directories (MPI/inc) +add_subdirectory (MPI) + include_directories (HistogramData/inc) add_subdirectory (HistogramData) diff --git a/Framework/MPI/CMakeLists.txt b/Framework/MPI/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..007d491a376aa09646ab923c6599854bd52d2fc5 --- /dev/null +++ b/Framework/MPI/CMakeLists.txt @@ -0,0 +1,59 @@ +set ( SRC_FILES + src/Communicator.cpp + src/CommunicatorBackend.cpp + src/ParallelRunner.cpp + src/Request.cpp + src/Status.cpp +) + +set ( INC_FILES + inc/MantidMPI/Communicator.h + inc/MantidMPI/CommunicatorBackend.h + inc/MantidMPI/ParallelRunner.h + inc/MantidMPI/Request.h + inc/MantidMPI/Status.h +) + +set ( TEST_FILES + CommunicatorBackendTest.h + CommunicatorTest.h + ParallelRunnerTest.h + RequestTest.h + StatusTest.h +) + +if (COVERALLS) + foreach( loop_var ${SRC_FILES} ${INC_FILES}) + set_property(GLOBAL APPEND PROPERTY COVERAGE_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/${loop_var}") + endforeach(loop_var) +endif() + +if(UNITY_BUILD) + include(UnityBuild) + enable_unity_build(MPI SRC_FILES SRC_UNITY_IGNORE_FILES 10) +endif(UNITY_BUILD) + +# Add the target for this directory +add_library ( MPI ${SRC_FILES} ${INC_FILES} ) +# Set the name of the generated library +set_target_properties ( MPI PROPERTIES OUTPUT_NAME MantidMPI + COMPILE_DEFINITIONS IN_MANTID_MPI ) + +if (OSX_VERSION VERSION_GREATER 10.8) + set_target_properties ( MPI PROPERTIES INSTALL_RPATH "@loader_path/../MacOS") +endif () + +# Add to the 'Framework' group in VS +set_property ( TARGET MPI PROPERTY FOLDER "MantidFramework" ) + +target_link_libraries ( MPI LINK_PRIVATE ${TCMALLOC_LIBRARIES_LINKTIME} + ${GSL_LIBRARIES} ${MANTIDLIBS} ) + +# Add the unit tests directory +add_subdirectory ( test ) + +########################################################################### +# Installation settings +########################################################################### + +install ( TARGETS MPI ${SYSTEM_PACKAGE_TARGET} DESTINATION ${LIB_DIR} ) diff --git a/Framework/MPI/inc/MantidMPI/Communicator.h b/Framework/MPI/inc/MantidMPI/Communicator.h new file mode 100644 index 0000000000000000000000000000000000000000..e0edf9933fa29380c0c961c34e571f97738eab43 --- /dev/null +++ b/Framework/MPI/inc/MantidMPI/Communicator.h @@ -0,0 +1,106 @@ +#ifndef MANTID_MPI_COMMUNICATOR_H_ +#define MANTID_MPI_COMMUNICATOR_H_ + +#include "MantidMPI/CommunicatorBackend.h" +#include "MantidMPI/DllConfig.h" +#include "MantidMPI/Status.h" + +#include <boost/make_shared.hpp> +#ifdef MPI_EXPERIMENTAL +#include <boost/mpi/communicator.hpp> +#endif + +namespace Mantid { +namespace MPI { + +/** Wrapper for boost::mpi::communicator. For non-MPI builds an equivalent + implementation is provided. + + @author Simon Heybrock + @date 2017 + + Copyright © 2017 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class MANTID_MPI_DLL Communicator { +public: + Communicator() = default; +#ifdef MPI_EXPERIMENTAL + explicit Communicator(const boost::mpi::communicator &comm); +#else + Communicator(boost::shared_ptr<CommunicatorBackend> backend, const int rank); +#endif + + int rank() const; + int size() const; + template <typename... T> void send(T &&... args) const; + template <typename... T> void recv(T &&... args) const; + template <typename... T> Request isend(T &&... args) const; + template <typename... T> Request irecv(T &&... args) const; + +private: +#ifdef MPI_EXPERIMENTAL + boost::mpi::communicator m_communicator; +#else + boost::shared_ptr<CommunicatorBackend> m_backend{ + boost::make_shared<CommunicatorBackend>()}; + const int m_rank{0}; +#endif +}; + +template <typename... T> void Communicator::send(T &&... args) const { +#ifdef MPI_EXPERIMENTAL + m_communicator.send(std::forward<T>(args)...); +#else + m_backend->send(m_rank, std::forward<T>(args)...); +#endif +} + +template <typename... T> void Communicator::recv(T &&... args) const { +#ifdef MPI_EXPERIMENTAL + // Not returning a status since it would usually not get initialized. See + // http://mpi-forum.org/docs/mpi-1.1/mpi-11-html/node35.html#Node35. + static_cast<void>(m_communicator.recv(std::forward<T>(args)...)); +#else + return m_backend->recv(m_rank, std::forward<T>(args)...); +#endif +} + +template <typename... T> Request Communicator::isend(T &&... args) const { +#ifdef MPI_EXPERIMENTAL + return m_communicator.isend(std::forward<T>(args)...); +#else + return m_backend->isend(m_rank, std::forward<T>(args)...); +#endif +} + +template <typename... T> Request Communicator::irecv(T &&... args) const { +#ifdef MPI_EXPERIMENTAL + return m_communicator.irecv(std::forward<T>(args)...); +#else + return m_backend->irecv(m_rank, std::forward<T>(args)...); +#endif +} + +} // namespace MPI +} // namespace Mantid + +#endif /* MANTID_MPI_COMMUNICATOR_H_ */ diff --git a/Framework/MPI/inc/MantidMPI/CommunicatorBackend.h b/Framework/MPI/inc/MantidMPI/CommunicatorBackend.h new file mode 100644 index 0000000000000000000000000000000000000000..d82a6cb532cb084ebf698c54187988d946ec9787 --- /dev/null +++ b/Framework/MPI/inc/MantidMPI/CommunicatorBackend.h @@ -0,0 +1,125 @@ +#ifndef MANTID_MPI_COMMUNICATORBACKEND_H_ +#define MANTID_MPI_COMMUNICATORBACKEND_H_ + +#include "MantidMPI/DllConfig.h" +#include "MantidMPI/Request.h" +#include "MantidKernel/make_unique.h" + +#include <boost/archive/binary_oarchive.hpp> +#include <boost/archive/binary_iarchive.hpp> + +#include <functional> +#include <istream> +#include <map> +#include <mutex> +#include <ostream> +#include <sstream> +#include <tuple> + +namespace Mantid { +namespace MPI { + +/** CommunicatorBackend provides a backend for data exchange between + Communicators in the case of non-MPI builds when communication between threads + is used to mimic MPI calls. + + @author Simon Heybrock + @date 2017 + + Copyright © 2017 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class MANTID_MPI_DLL CommunicatorBackend { +public: + CommunicatorBackend() = default; + explicit CommunicatorBackend(const int size); + + CommunicatorBackend(const CommunicatorBackend &) = delete; + const CommunicatorBackend &operator=(const CommunicatorBackend &) = delete; + + int size() const; + + template <typename... T> + void send(int source, int dest, int tag, T &&... args); + + template <typename... T> + void recv(int dest, int source, int tag, T &&... args); + + template <typename... T> + Request isend(int source, int dest, int tag, T &&... args); + + template <typename... T> + Request irecv(int dest, int source, int tag, T &&... args); + +private: + int m_size{1}; + std::map<std::tuple<int, int, int>, std::vector<std::stringbuf>> m_buffer; + std::mutex m_mutex; +}; + +template <typename... T> +void CommunicatorBackend::send(int source, int dest, int tag, T &&... args) { + std::stringbuf buf; + std::ostream os(&buf); + boost::archive::binary_oarchive oa(os); + oa.operator<<(std::forward<T>(args)...); + std::lock_guard<std::mutex> lock(m_mutex); + m_buffer[std::make_tuple(source, dest, tag)].push_back(std::move(buf)); +} + +template <typename... T> +void CommunicatorBackend::recv(int dest, int source, int tag, T &&... args) { + const auto key = std::make_tuple(source, dest, tag); + while (true) { + std::lock_guard<std::mutex> lock(m_mutex); + auto it = m_buffer.find(key); + if (it == m_buffer.end()) + continue; + auto &queue = it->second; + if (queue.empty()) + continue; + std::istream is(&queue.front()); + boost::archive::binary_iarchive ia(is); + ia.operator>>(std::forward<T>(args)...); + queue.erase(queue.begin()); + std::cout.operator<<(args...) << std::endl; + return; + } +} + +template <typename... T> +Request CommunicatorBackend::isend(int source, int dest, int tag, + T &&... args) { + send(source, dest, tag, std::forward<T>(args)...); + return Request{}; +} + +template <typename... T> +Request CommunicatorBackend::irecv(int dest, int source, int tag, + T &&... args) { + return Request(std::bind(&CommunicatorBackend::recv<T...>, this, dest, source, + tag, std::ref(std::forward<T>(args)...))); +} + +} // namespace MPI +} // namespace Mantid + +#endif /* MANTID_MPI_COMMUNICATORBACKEND_H_ */ diff --git a/Framework/MPI/inc/MantidMPI/DllConfig.h b/Framework/MPI/inc/MantidMPI/DllConfig.h new file mode 100644 index 0000000000000000000000000000000000000000..d689081f7872224394b720a00ff05ffe49c6d7c9 --- /dev/null +++ b/Framework/MPI/inc/MantidMPI/DllConfig.h @@ -0,0 +1,37 @@ +#ifndef MANTID_MPI_DLLCONFIG_H_ +#define MANTID_MPI_DLLCONFIG_H_ + +/* + This file contains the DLLExport/DLLImport linkage configuration for the + MPI library + + Copyright © 2016 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +#include "MantidKernel/System.h" + +#ifdef IN_MANTID_MPI +#define MANTID_MPI_DLL DLLExport +#else +#define MANTID_MPI_DLL DLLImport +#endif // IN_MANTID_MPI + +#endif // MANTID_MPI_DLLCONFIG_H_ diff --git a/Framework/MPI/inc/MantidMPI/ParallelRunner.h b/Framework/MPI/inc/MantidMPI/ParallelRunner.h new file mode 100644 index 0000000000000000000000000000000000000000..d5c26d3c36ec6641372a717e4063744c21a05d55 --- /dev/null +++ b/Framework/MPI/inc/MantidMPI/ParallelRunner.h @@ -0,0 +1,82 @@ +#ifndef MANTID_MPI_PARALLELRUNNER_H_ +#define MANTID_MPI_PARALLELRUNNER_H_ + +#include "MantidMPI/CommunicatorBackend.h" +#include "MantidMPI/Communicator.h" +#include "MantidMPI/DllConfig.h" + +#include <functional> +#include <thread> + +namespace Mantid { +namespace MPI { + +/** Runs a callable in parallel. This is mainly a helper for testing code with + MPI calls. ParallelRunner passes a Communicator as first argument to the + callable. In a non-MPI build the callable is executed in threads ot mimic MPI + ranks. + + Copyright © 2017 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class MANTID_MPI_DLL ParallelRunner { +public: + ParallelRunner(); + ParallelRunner(const int threads); + + int size() const; + + template <class Function, class... Args> + void run(Function &&f, Args &&... args); + +private: +#ifndef MPI_EXPERIMENTAL + boost::shared_ptr<CommunicatorBackend> m_backend; +#endif +}; + +template <class Function, class... Args> +void ParallelRunner::run(Function &&f, Args &&... args) { +#ifdef MPI_EXPERIMENTAL + Communicator comm; + f(comm, std::forward<Args>(args)...); +#else + std::vector<std::thread> threads; + for (int t = 0; t < m_backend->size(); ++t) { + Communicator comm(m_backend, t); + threads.emplace_back(std::forward<Function>(f), comm, + std::forward<Args>(args)...); + } + for (auto &t : threads) { + t.join(); + } +#endif +} + +template <class... Args> void runParallel(Args &&... args) { + ParallelRunner runner; + runner.run(std::forward<Args>(args)...); +} + +} // namespace MPI +} // namespace Mantid + +#endif /* MANTID_MPI_PARALLELRUNNER_H_ */ diff --git a/Framework/MPI/inc/MantidMPI/Request.h b/Framework/MPI/inc/MantidMPI/Request.h new file mode 100644 index 0000000000000000000000000000000000000000..b45c7fcff1df7d3e7d7ba9b627421871920ad2ef --- /dev/null +++ b/Framework/MPI/inc/MantidMPI/Request.h @@ -0,0 +1,70 @@ +#ifndef MANTID_MPI_REQUEST_H_ +#define MANTID_MPI_REQUEST_H_ + +#include "MantidMPI/DllConfig.h" + +#ifdef MPI_EXPERIMENTAL +#include <boost/mpi/request.hpp> +#else +#include <thread> +#endif + +namespace Mantid { +namespace MPI { + +/** Wrapper for boost::mpi::request. For non-MPI builds an equivalent + implementation is provided. + + @author Simon Heybrock + @date 2017 + + Copyright © 2017 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class MANTID_MPI_DLL Request { +public: + Request() = default; +#ifdef MPI_EXPERIMENTAL + Request(const boost::mpi::request &request); +#else + template <class Function> explicit Request(Function &&f); +#endif + + void wait(); + +private: +#ifdef MPI_EXPERIMENTAL + boost::mpi::request m_request; +#else + std::thread m_thread; +#endif +}; + +#ifndef MPI_EXPERIMENTAL +template <class Function> +Request::Request(Function &&f) + : m_thread(std::forward<Function>(f)) {} +#endif + +} // namespace MPI +} // namespace Mantid + +#endif /* MANTID_MPI_REQUEST_H_ */ diff --git a/Framework/MPI/inc/MantidMPI/Status.h b/Framework/MPI/inc/MantidMPI/Status.h new file mode 100644 index 0000000000000000000000000000000000000000..a5a79933da9c8aa9badc4a42a9a9edf9e707ea85 --- /dev/null +++ b/Framework/MPI/inc/MantidMPI/Status.h @@ -0,0 +1,65 @@ +#ifndef MANTID_MPI_STATUS_H_ +#define MANTID_MPI_STATUS_H_ + +#include "MantidMPI/DllConfig.h" + +#ifdef MPI_EXPERIMENTAL +#include <boost/mpi/status.hpp> +#endif + +namespace Mantid { +namespace MPI { + +/** Wrapper for boost::mpi::status. For non-MPI builds an equivalent + implementation is provided. + + @author Simon Heybrock + @date 2017 + + Copyright © 2017 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge + National Laboratory & European Spallation Source + + This file is part of Mantid. + + Mantid is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + Mantid is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + File change history is stored at: <https://github.com/mantidproject/mantid> + Code Documentation is available at: <http://doxygen.mantidproject.org> +*/ +class MANTID_MPI_DLL Status { +public: +#ifdef MPI_EXPERIMENTAL + Status(const boost::mpi::status &status); +#else + Status(int source, int tag, int error); +#endif + + int source() const; + int tag() const; + int error() const; + +private: +#ifdef MPI_EXPERIMENTAL + boost::mpi::status m_status; +#else + int m_source; + int m_tag; + int m_error; +#endif +}; + +} // namespace MPI +} // namespace Mantid + +#endif /* MANTID_MPI_STATUS_H_ */ diff --git a/Framework/MPI/src/Communicator.cpp b/Framework/MPI/src/Communicator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..06ffacd45bc64372510aad4805775b02e2f223e4 --- /dev/null +++ b/Framework/MPI/src/Communicator.cpp @@ -0,0 +1,32 @@ +#include "MantidMPI/Communicator.h" + +namespace Mantid { +namespace MPI { + +#ifdef MPI_EXPERIMENTAL +Communicator::Communicator(const boost::mpi::communicator &comm) + : m_communicator(comm) {} +#else +Communicator::Communicator(boost::shared_ptr<CommunicatorBackend> backend, + const int rank) + : m_backend(backend), m_rank(rank) {} +#endif + +int Communicator::rank() const { +#ifdef MPI_EXPERIMENTAL + return m_communicator.rank(); +#else + return m_rank; +#endif +} + +int Communicator::size() const { +#ifdef MPI_EXPERIMENTAL + return m_communicator.size(); +#else + return m_backend->size(); +#endif +} + +} // namespace MPI +} // namespace Mantid diff --git a/Framework/MPI/src/CommunicatorBackend.cpp b/Framework/MPI/src/CommunicatorBackend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23a79d74cafdfde898d43a02d61a1c88962d5a8e --- /dev/null +++ b/Framework/MPI/src/CommunicatorBackend.cpp @@ -0,0 +1,11 @@ +#include "MantidMPI/CommunicatorBackend.h" + +namespace Mantid { +namespace MPI { + +CommunicatorBackend::CommunicatorBackend(const int size) : m_size(size) {} + +int CommunicatorBackend::size() const { return m_size; } + +} // namespace MPI +} // namespace Mantid diff --git a/Framework/MPI/src/ParallelRunner.cpp b/Framework/MPI/src/ParallelRunner.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a2c77799c457c3bc19846a2cbf8b760ecc337c5 --- /dev/null +++ b/Framework/MPI/src/ParallelRunner.cpp @@ -0,0 +1,36 @@ +#include "MantidMPI/ParallelRunner.h" + +#include <omp.h> + +namespace Mantid { +namespace MPI { + +ParallelRunner::ParallelRunner() { +#ifndef MPI_EXPERIMENTAL + m_backend = boost::make_shared<CommunicatorBackend>(omp_get_max_threads()); +#endif +} + +ParallelRunner::ParallelRunner(const int threads) { +#ifdef MPI_EXPERIMENTAL + Communicator comm; + if (comm.size() != threads) + throw( + "ParallelRunner: number of requested threads does not match number of " + "MPI ranks"); +#else + m_backend = boost::make_shared<CommunicatorBackend>(threads); +#endif +} + +int ParallelRunner::size() const { +#ifdef MPI_EXPERIMENTAL + Communicator comm; + return comm.size(); +#else + return m_backend->size(); +#endif +} + +} // namespace MPI +} // namespace Mantid diff --git a/Framework/MPI/src/Request.cpp b/Framework/MPI/src/Request.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c37ce1db3be85c116fe85cf28aab0e9dfb06c0de --- /dev/null +++ b/Framework/MPI/src/Request.cpp @@ -0,0 +1,26 @@ +#include "MantidMPI/Request.h" + +#ifdef MPI_EXPERIMENTAL +#include <boost/mpi/status.hpp> +#endif + +namespace Mantid { +namespace MPI { + +#ifdef MPI_EXPERIMENTAL +Request::Request(const boost::mpi::request &request) : m_request(request) {} +#endif + +void Request::wait() { +// Not returning a status since it would usually not get initialized. See +// http://mpi-forum.org/docs/mpi-1.1/mpi-11-html/node35.html#Node35. +#ifdef MPI_EXPERIMENTAL + static_cast<void>(m_request.wait()); +#else + if (m_thread.joinable()) + m_thread.join(); +#endif +} + +} // namespace MPI +} // namespace Mantid diff --git a/Framework/MPI/src/Status.cpp b/Framework/MPI/src/Status.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e3311af865b8c98a2d9fa7493f3c545312a338ef --- /dev/null +++ b/Framework/MPI/src/Status.cpp @@ -0,0 +1,38 @@ +#include "MantidMPI/Status.h" + +namespace Mantid { +namespace MPI { + +#ifdef MPI_EXPERIMENTAL +Status::Status(const boost::mpi::status &status) : m_status(status) {} +#else +Status::Status(int source, int tag, int error) + : m_source(source), m_tag(tag), m_error(error) {} +#endif + +int Status::source() const { +#ifdef MPI_EXPERIMENTAL + return m_status.source(); +#else + return m_source; +#endif +} + +int Status::tag() const { +#ifdef MPI_EXPERIMENTAL + return m_status.tag(); +#else + return m_tag; +#endif +} + +int Status::error() const { +#ifdef MPI_EXPERIMENTAL + return m_status.error(); +#else + return m_error; +#endif +} + +} // namespace MPI +} // namespace Mantid diff --git a/Framework/MPI/test/CMakeLists.txt b/Framework/MPI/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0cbe1d7e48dfc659afae82d01c94bc1f3d6b2970 --- /dev/null +++ b/Framework/MPI/test/CMakeLists.txt @@ -0,0 +1,18 @@ +if ( CXXTEST_FOUND ) + include_directories ( SYSTEM ${CXXTEST_INCLUDE_DIR} ${GMOCK_INCLUDE_DIR} ${GTEST_INCLUDE_DIR} ../../TestHelpers/inc) + + # This variable is used within the cxxtest_add_test macro to build these helper classes into the test executable. + # It will go out of scope at the end of this file so doesn't need un-setting + set ( TESTHELPER_SRCS EnvironmentSetup.cpp ) + + cxxtest_add_test ( MPITest ${TEST_FILES} ${GMOCK_TEST_FILES}) + target_link_libraries( MPITest LINK_PRIVATE ${TCMALLOC_LIBRARIES_LINKTIME} ${MANTIDLIBS} + MPI + ${Boost_LIBRARIES} + ${GMOCK_LIBRARIES} + ${GTEST_LIBRARIES} ) + + add_dependencies ( FrameworkTests MPITest ) + # Add to the 'FrameworkTests' group in VS + set_property ( TARGET MPITest PROPERTY FOLDER "UnitTests" ) +endif () diff --git a/Framework/MPI/test/CommunicatorBackendTest.h b/Framework/MPI/test/CommunicatorBackendTest.h new file mode 100644 index 0000000000000000000000000000000000000000..60ca4ac9768dd9147c8e549d1f7ffadbd69e993e --- /dev/null +++ b/Framework/MPI/test/CommunicatorBackendTest.h @@ -0,0 +1,30 @@ +#ifndef MANTID_MPI_COMMUNICATORBACKENDTEST_H_ +#define MANTID_MPI_COMMUNICATORBACKENDTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidMPI/CommunicatorBackend.h" + +using Mantid::MPI::CommunicatorBackend; + +class CommunicatorBackendTest : public CxxTest::TestSuite { +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static CommunicatorBackendTest *createSuite() { + return new CommunicatorBackendTest(); + } + static void destroySuite(CommunicatorBackendTest *suite) { delete suite; } + + void test_default_constructor() { + CommunicatorBackend comm; + TS_ASSERT_EQUALS(comm.size(), 1); + } + + void test_size_constructor() { + CommunicatorBackend comm{2}; + TS_ASSERT_EQUALS(comm.size(), 2); + } +}; + +#endif /* MANTID_MPI_COMMUNICATORBACKENDTEST_H_ */ diff --git a/Framework/MPI/test/CommunicatorTest.h b/Framework/MPI/test/CommunicatorTest.h new file mode 100644 index 0000000000000000000000000000000000000000..3d8518fd651856aef1c89d58f8e9b0d8a3b2aef6 --- /dev/null +++ b/Framework/MPI/test/CommunicatorTest.h @@ -0,0 +1,101 @@ +#ifndef MANTID_MPI_COMMUNICATORTEST_H_ +#define MANTID_MPI_COMMUNICATORTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidMPI/Communicator.h" +#include "MantidMPI/ParallelRunner.h" + +using namespace Mantid::MPI; + +namespace { +void send_recv(const Communicator &comm) { + if (comm.size() < 2) + return; + + double data = 3.14; + + if (comm.rank() == 0) + TS_ASSERT_THROWS_NOTHING(comm.send(1, 123, data)); + if (comm.rank() == 1) { + double result; + TS_ASSERT_THROWS_NOTHING(comm.recv(0, 123, result)); + TS_ASSERT_EQUALS(result, data); + } +} + +void isend_recv(const Communicator &comm) { + int64_t data = 123456789 + comm.rank(); + int dest = (comm.rank() + 1) % comm.size(); + int src = (comm.rank() + comm.size() - 1) % comm.size(); + int tag = 123; + int64_t result; + int64_t expected = comm.rank() == 0 ? data + comm.size() - 1 : data - 1; + + auto request = comm.isend(dest, tag, data); + TS_ASSERT_THROWS_NOTHING(comm.recv(src, tag, result)); + TS_ASSERT_EQUALS(result, expected); + TS_ASSERT_THROWS_NOTHING(request.wait()); +} + +void send_irecv(const Communicator &comm) { + int64_t data = 123456789 + comm.rank(); + int dest = (comm.rank() + 1) % comm.size(); + int src = (comm.rank() + comm.size() - 1) % comm.size(); + int tag = 123; + int64_t result; + int64_t expected = comm.rank() == 0 ? data + comm.size() - 1 : data - 1; + + auto request = comm.irecv(src, tag, result); + TS_ASSERT_THROWS_NOTHING(comm.send(dest, tag, data)); + TS_ASSERT_THROWS_NOTHING(request.wait()); + TS_ASSERT_EQUALS(result, expected); +} + +void isend_irecv(const Communicator &comm) { + int64_t data = 123456789 + comm.rank(); + int dest = (comm.rank() + 1) % comm.size(); + int src = (comm.rank() + comm.size() - 1) % comm.size(); + int tag = 123; + int64_t result; + int64_t expected = comm.rank() == 0 ? data + comm.size() - 1 : data - 1; + + auto send_req = comm.irecv(src, tag, result); + auto recv_req = comm.isend(dest, tag, data); + TS_ASSERT_THROWS_NOTHING(send_req.wait()); + TS_ASSERT_THROWS_NOTHING(recv_req.wait()); + TS_ASSERT_EQUALS(result, expected); +} +} + +class CommunicatorTest : public CxxTest::TestSuite { +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static CommunicatorTest *createSuite() { return new CommunicatorTest(); } + static void destroySuite(CommunicatorTest *suite) { delete suite; } + + void test_construct_default() { TS_ASSERT_THROWS_NOTHING(Communicator{}); } + + void test_defaults() { + Communicator comm; +#ifdef MPI_EXPERIMENTAL + boost::mpi::communicator world; + TS_ASSERT_EQUALS(comm.size(), world.size()); + TS_ASSERT_EQUALS(comm.rank(), world.rank()); +#else + TS_ASSERT_EQUALS(comm.size(), 1); + TS_ASSERT_EQUALS(comm.rank(), 0); +#endif + } + + void test_send_recv() { runParallel(send_recv); } + + void test_isend_recv() { runParallel(isend_recv); } + + void test_send_irecv() { runParallel(send_irecv); } + + void test_isend_irecv() { runParallel(isend_irecv); } +}; + +#endif /* MANTID_MPI_COMMUNICATORTEST_H_ */ diff --git a/Framework/MPI/test/EnvironmentSetup.cpp b/Framework/MPI/test/EnvironmentSetup.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5fa2467672baffffce717911785be8bca2ee78cc --- /dev/null +++ b/Framework/MPI/test/EnvironmentSetup.cpp @@ -0,0 +1,9 @@ +#ifndef MANTID_MPI_ENVIRONMENTSETUP_H_ +#define MANTID_MPI_ENVIRONMENTSETUP_H_ + +#ifdef MPI_EXPERIMENTAL +#include <boost/mpi/environment.hpp> +boost::mpi::environment env; +#endif + +#endif /* MANTID_MPI_ENVIRONMENTSETUP_H_ */ diff --git a/Framework/MPI/test/ParallelRunnerTest.h b/Framework/MPI/test/ParallelRunnerTest.h new file mode 100644 index 0000000000000000000000000000000000000000..5035f4ac8220ec398cdae1014ff12443209d615d --- /dev/null +++ b/Framework/MPI/test/ParallelRunnerTest.h @@ -0,0 +1,53 @@ +#ifndef MANTID_MPI_PARALLELRUNNERTEST_H_ +#define MANTID_MPI_PARALLELRUNNERTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidMPI/ParallelRunner.h" + +#include <mutex> +#include <vector> + +using namespace Mantid::MPI; + +namespace { +void check_size(const Communicator &comm, const int expected) { + TS_ASSERT_EQUALS(comm.size(), expected); +} + +void get_ranks(const Communicator &comm, std::mutex &mutex, + std::set<int> &ranks) { + std::lock_guard<std::mutex> lock(mutex); + ranks.insert(comm.rank()); +} +} + +class ParallelRunnerTest : public CxxTest::TestSuite { +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static ParallelRunnerTest *createSuite() { return new ParallelRunnerTest(); } + static void destroySuite(ParallelRunnerTest *suite) { delete suite; } + + void test_size() { + ParallelRunner parallel; + parallel.run(check_size, parallel.size()); + } + + void test_rank() { + std::mutex mutex; + std::set<int> ranks; + ParallelRunner parallel; + parallel.run(get_ranks, std::ref(mutex), std::ref(ranks)); +#ifdef MPI_EXPERIMENTAL + boost::mpi::communicator world; + TS_ASSERT_EQUALS(ranks.size(), 1); + TS_ASSERT_EQUALS(ranks.count(world.rank()), 1); +#else + for (int rank = 0; rank < parallel.size(); ++rank) + TS_ASSERT_EQUALS(ranks.count(rank), 1); +#endif + } +}; + +#endif /* MANTID_MPI_PARALLELRUNNERTEST_H_ */ diff --git a/Framework/MPI/test/RequestTest.h b/Framework/MPI/test/RequestTest.h new file mode 100644 index 0000000000000000000000000000000000000000..a98190350dd8998973b184c5c920f179806108c7 --- /dev/null +++ b/Framework/MPI/test/RequestTest.h @@ -0,0 +1,24 @@ +#ifndef MANTID_MPI_REQUESTTEST_H_ +#define MANTID_MPI_REQUESTTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidMPI/Request.h" + +using Mantid::MPI::Request; + +class RequestTest : public CxxTest::TestSuite { +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static RequestTest *createSuite() { return new RequestTest(); } + static void destroySuite(RequestTest *suite) { delete suite; } + + void test_default() { + TS_ASSERT_THROWS_NOTHING(Request()); + Request req; + TS_ASSERT_THROWS_NOTHING(req.wait()); + } +}; + +#endif /* MANTID_MPI_REQUESTTEST_H_ */ diff --git a/Framework/MPI/test/StatusTest.h b/Framework/MPI/test/StatusTest.h new file mode 100644 index 0000000000000000000000000000000000000000..49c67cdbb07d44da0c2e74c9f0380916226294da --- /dev/null +++ b/Framework/MPI/test/StatusTest.h @@ -0,0 +1,32 @@ +#ifndef MANTID_MPI_STATUSTEST_H_ +#define MANTID_MPI_STATUSTEST_H_ + +#include <cxxtest/TestSuite.h> + +#include "MantidMPI/Status.h" + +using Mantid::MPI::Status; + +class StatusTest : public CxxTest::TestSuite { +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static StatusTest *createSuite() { return new StatusTest(); } + static void destroySuite(StatusTest *suite) { delete suite; } + + void test_constructor() { +#ifdef MPI_EXPERIMENTAL + Status status(boost::mpi::status{}); + TS_ASSERT_EQUALS(status.source(), 0); + TS_ASSERT_EQUALS(status.tag(), 0); + TS_ASSERT_EQUALS(status.error(), 0); +#else + Status status(1, 2, 3); + TS_ASSERT_EQUALS(status.source(), 1); + TS_ASSERT_EQUALS(status.tag(), 2); + TS_ASSERT_EQUALS(status.error(), 3); +#endif + } +}; + +#endif /* MANTID_MPI_STATUSTEST_H_ */