From 9dbb46d716418121fccf7df06d1e37ba63b8d0f5 Mon Sep 17 00:00:00 2001 From: nanmiaowu Date: Mon, 28 Mar 2022 12:01:42 -0500 Subject: [PATCH] adding hpx support to master branch of pnnl --- CMakeLists.txt | 2 +- cmake/config.cmake | 6 + examples/pi/pi.cc | 39 +- include/shad/core/impl/impl_patterns.h | 84 +++ include/shad/core/iterator.h | 21 + .../shad/data_structures/object_identifier.h | 4 + .../extensions/graph_library/edge_index.h | 3 + .../graph_library/local_edge_index.h | 6 + include/shad/runtime/handle.h | 20 + .../runtime/mappings/available_mappings.h | 3 + .../mappings/available_traits_mappings.h | 2 + .../mappings/hpx/hpx_asynchronous_interface.h | 390 +++++++++++ .../mappings/hpx/hpx_synchronous_interface.h | 351 ++++++++++ .../runtime/mappings/hpx/hpx_traits_mapping.h | 108 +++ .../shad/runtime/mappings/hpx/hpx_utility.h | 628 ++++++++++++++++++ include/shad/runtime/runtime.h | 30 + src/runtime/CMakeLists.txt | 17 +- src/runtime/hpx_mapping/hpx_main.cc | 41 ++ test/unit_tests/core/shad_algorithm_test.cc | 20 + test/unit_tests/core/shad_numeric_test.cc | 8 + .../unit_tests/data_structures/atomic_test.cc | 25 +- test/unit_tests/runtime/CMakeLists.txt | 6 +- 22 files changed, 1788 insertions(+), 26 deletions(-) create mode 100644 include/shad/runtime/mappings/hpx/hpx_asynchronous_interface.h create mode 100644 include/shad/runtime/mappings/hpx/hpx_synchronous_interface.h create mode 100644 include/shad/runtime/mappings/hpx/hpx_traits_mapping.h create mode 100644 include/shad/runtime/mappings/hpx/hpx_utility.h create mode 100644 src/runtime/hpx_mapping/hpx_main.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e8864d4..ec5474ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,7 +78,7 @@ option(SHAD_ENABLE_PERFORMANCE_TEST "Enable the compilation of the Performance T set( SHAD_RUNTIME_SYSTEM "CPP_SIMPLE" CACHE STRING - "(Default) Runtime system to be used as backend of the Abstract Runtime API (Default=CPP_SIMPLE, Supported=CPP_SIMPLE | TBB | GMT)") + "(Default) Runtime system to be used as backend of the Abstract Runtime API (Default=CPP_SIMPLE, Supported=CPP_SIMPLE | TBB | GMT | HPX)") include(config) diff --git a/cmake/config.cmake b/cmake/config.cmake index 5fee00b2..74387c00 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -98,6 +98,12 @@ elseif (SHAD_RUNTIME_SYSTEM STREQUAL "GMT") include_directories(${GMT_INCLUDE_DIR}) set(HAVE_GMT 1) set(SHAD_RUNTIME_LIB ${GMT_LIBRARIES}) +elseif (SHAD_RUNTIME_SYSTEM STREQUAL "HPX") + message(STATUS "Using HPX as backend of the Abstract Runtime API.") + find_package(HPX REQUIRED) + include_directories(${HPX_INCLUDE_DIR}) + set(HAVE_HPX 1) + set(SHAD_RUNTIME_LIB HPX::hpx) else() message(FATAL_ERROR "${SHAD_RUNTIME_SYSTEM} is not a supported runtime system.") endif() diff --git a/examples/pi/pi.cc b/examples/pi/pi.cc index a64f6d05..61c21fbe 100644 --- a/examples/pi/pi.cc +++ b/examples/pi/pi.cc @@ -39,25 +39,30 @@ int main(int argc, char *argv[]) { const size_t numberOfPoints = 1e10; const size_t numberOfPointsPerSim = numberOfPoints / counters.size(); - shad::generate( - shad::distributed_parallel_tag{}, - counters.begin(), counters.end(), - [=]() -> uint64_t { - size_t counter = 0; + auto generator = [=]() -> uint64_t { + size_t counter = 0; - std::random_device rd; - std::default_random_engine G(rd()); - std::uniform_real_distribution dist(0.0, 1.0); + std::random_device rd; + std::default_random_engine G(rd()); + std::uniform_real_distribution dist(0.0, 1.0); - for (size_t i = 0; i < numberOfPointsPerSim; ++i) { - double x = dist(G); - double y = dist(G); - if ((x * x + y * y) < 1) { - ++counter; - } - } - return counter; - }); + for (size_t i = 0; i < numberOfPointsPerSim; ++i) { + double x = dist(G); + double y = dist(G); + if ((x * x + y * y) < 1) { + ++counter; + } + } + return counter; + }; + +#ifdef HAVE_HPX + shad::generate(shad::distributed_parallel_tag{}, counters.begin(), + counters.end(), shad::rt::lambda_wrapper(generator)); +#else + shad::generate(shad::distributed_parallel_tag{}, counters.begin(), + counters.end(), generator); +#endif uint64_t count = shad::reduce( shad::distributed_parallel_tag{}, counters.begin(), counters.end()); diff --git a/include/shad/core/impl/impl_patterns.h b/include/shad/core/impl/impl_patterns.h index a37ed48b..f0341d38 100755 --- a/include/shad/core/impl/impl_patterns.h +++ b/include/shad/core/impl/impl_patterns.h @@ -92,6 +92,21 @@ S distributed_folding_map(ForwardIt first, ForwardIt last, MapF&& map_kernel, using itr_traits = distributed_iterator_traits; auto localities = itr_traits::localities(first, last); auto res = init_sol; +#ifdef HAVE_HPX + for (auto locality = localities.begin(), end = localities.end(); + locality != end; ++locality) { + auto d_args = std::make_tuple(shad::rt::lambda_wrapper>( + std::forward(map_kernel)), + first, last, res, args...); + rt::executeAtWithRet( + locality, + [](const typeof(d_args)& d_args, S* result) { + *result = apply_from<1>(::std::get<0>(d_args), + ::std::forward(d_args)); + }, + d_args, &res); + } +#else for (auto locality = localities.begin(), end = localities.end(); locality != end; ++locality) { auto d_args = std::make_tuple(map_kernel, first, last, res, args...); @@ -103,6 +118,7 @@ S distributed_folding_map(ForwardIt first, ForwardIt last, MapF&& map_kernel, }, d_args, &res); } +#endif return res; } @@ -112,6 +128,21 @@ void distributed_folding_map_void(ForwardIt first, ForwardIt last, MapF&& map_kernel, Args&&... args) { using itr_traits = distributed_iterator_traits; auto localities = itr_traits::localities(first, last); +#ifdef HAVE_HPX + for (auto locality = localities.begin(), end = localities.end(); + locality != end; ++locality) { + auto d_args = std::make_tuple(shad::rt::lambda_wrapper>( + std::forward(map_kernel)), + first, last, args...); + rt::executeAt( + locality, + [](const typeof(d_args)& d_args) { + apply_from<1>(::std::get<0>(d_args), + ::std::forward(d_args)); + }, + d_args); + } +#else for (auto locality = localities.begin(), end = localities.end(); locality != end; ++locality) { auto d_args = std::make_tuple(map_kernel, first, last, args...); @@ -123,6 +154,7 @@ void distributed_folding_map_void(ForwardIt first, ForwardIt last, }, d_args); } +#endif } // distributed_folding_map variant testing for early termination @@ -134,6 +166,22 @@ S distributed_folding_map_early_termination(ForwardIt first, ForwardIt last, using itr_traits = distributed_iterator_traits; auto localities = itr_traits::localities(first, last); auto res = init_sol; +#ifdef HAVE_HPX + for (auto locality = localities.begin(), end = localities.end(); + locality != end; ++locality) { + auto d_args = std::make_tuple(shad::rt::lambda_wrapper>( + std::forward(map_kernel)), + first, last, res, args...); + rt::executeAtWithRet( + locality, + [](const typeof(d_args)& d_args, S* result) { + *result = apply_from<1>(::std::get<0>(d_args), + ::std::forward(d_args)); + }, + d_args, &res); + if (halt(res)) return res; + } +#else for (auto locality = localities.begin(), end = localities.end(); locality != end; ++locality) { auto d_args = std::make_tuple(map_kernel, first, last, res, args...); @@ -146,6 +194,8 @@ S distributed_folding_map_early_termination(ForwardIt first, ForwardIt last, d_args, &res); if (halt(res)) return res; } +#endif + return res; } @@ -205,7 +255,13 @@ distributed_map_init( auto localities = itr_traits::localities(first, last); size_t i = 0; rt::Handle h; +#ifdef HAVE_HPX + auto d_args = std::make_tuple(shad::rt::lambda_wrapper>( + std::forward(map_kernel)), + first, last, args...); +#else auto d_args = std::make_tuple(map_kernel, first, last, args...); +#endif optional_vector opt_res(localities.size(), init); for (auto locality = localities.begin(), end = localities.end(); locality != end; ++locality, ++i) { @@ -255,7 +311,13 @@ void distributed_map_void(ForwardIt first, ForwardIt last, MapF&& map_kernel, auto localities = itr_traits::localities(first, last); size_t i = 0; rt::Handle h; +#ifdef HAVE_HPX + auto d_args = std::make_tuple(shad::rt::lambda_wrapper>( + std::forward(map_kernel)), + first, last, args...); +#else auto d_args = std::make_tuple(map_kernel, first, last, args...); +#endif for (auto locality = localities.begin(), end = localities.end(); locality != end; ++locality, ++i) { rt::asyncExecuteAt( @@ -301,7 +363,15 @@ local_map_init( std::vector map_res(parts.size(), init); if (parts.size()) { +#ifdef HAVE_HPX + auto map_args = + std::make_tuple(parts.data(), + shad::rt::lambda_wrapper>( + std::forward(map_kernel)), + map_res.data()); +#else auto map_args = std::make_tuple(parts.data(), map_kernel, map_res.data()); +#endif shad::rt::forEachAt( rt::thisLocality(), [](const typeof(map_args)& map_args, size_t iter) { @@ -339,7 +409,13 @@ void local_map_void(ForwardIt first, ForwardIt last, MapF&& map_kernel) { first, last, rt::impl::getConcurrency()); if (parts.size()) { +#ifdef HAVE_HPX + auto map_args = std::make_tuple( + parts.data(), shad::rt::lambda_wrapper>( + std::forward(map_kernel))); +#else auto map_args = std::make_tuple(parts.data(), map_kernel); +#endif shad::rt::forEachAt( rt::thisLocality(), [](const typeof(map_args)& map_args, size_t iter) { @@ -361,7 +437,15 @@ void local_map_void_offset(ForwardIt first, ForwardIt last, MapF&& map_kernel) { first, last, rt::impl::getConcurrency()); if (parts.size()) { +#ifdef HAVE_HPX + auto map_args = + std::make_tuple(parts.data(), + shad::rt::lambda_wrapper>( + std::forward(map_kernel)), + first); +#else auto map_args = std::make_tuple(parts.data(), map_kernel, first); +#endif shad::rt::forEachAt( rt::thisLocality(), [](const typeof(map_args)& map_args, size_t iter) { diff --git a/include/shad/core/iterator.h b/include/shad/core/iterator.h index b033e05a..2b06c795 100644 --- a/include/shad/core/iterator.h +++ b/include/shad/core/iterator.h @@ -29,6 +29,9 @@ #include #include "shad/runtime/runtime.h" +#ifdef HAVE_HPX +#include "hpx/serialization.hpp" +#endif namespace shad { @@ -59,6 +62,9 @@ class insert_iterator insert_iterator(Container& container, Iterator iterator) : global_id_(container.global_id()), iterator_(iterator) {} +#ifdef HAVE_HPX + insert_iterator() = default; +#endif /// @brief The assignment operator. /// /// The assignment operator inserts a value (through buffering) and advance @@ -114,6 +120,10 @@ class buffered_insert_iterator : public insert_iterator { buffered_insert_iterator(Container& container, Iterator iterator) : base_t(container, iterator) {} +#ifdef HAVE_HPX + buffered_insert_iterator() = default; +#endif + /// @brief The assignment operator. /// /// The assignment operator inserts a value (through buffering) and advance @@ -152,6 +162,17 @@ class buffered_insert_iterator : public insert_iterator { buffered_insert_iterator& operator++() { return *this; } buffered_insert_iterator& operator++(int) { return *this; } + #ifdef HAVE_HPX + private: + friend class hpx::serialization::access; + + template + void serialize(Archive& ar, unsigned) + { + ar & handle_; + } +#endif + private: rt::Handle handle_; }; diff --git a/include/shad/data_structures/object_identifier.h b/include/shad/data_structures/object_identifier.h index 4b7496bd..5768b255 100644 --- a/include/shad/data_structures/object_identifier.h +++ b/include/shad/data_structures/object_identifier.h @@ -56,7 +56,11 @@ class ObjectIdentifier { static constexpr uint8_t kIdentifierBitsize = 48u; /// @brief Constructor. +#ifdef HAVE_HPX + explicit constexpr ObjectIdentifier(uint64_t id = 0) : id_(id) {} +#else explicit constexpr ObjectIdentifier(uint64_t id) : id_(id) {} +#endif /// @brief Constructor. /// @param[in] locality The locality identifier part. diff --git a/include/shad/extensions/graph_library/edge_index.h b/include/shad/extensions/graph_library/edge_index.h index fd1e9680..8829dc89 100755 --- a/include/shad/extensions/graph_library/edge_index.h +++ b/include/shad/extensions/graph_library/edge_index.h @@ -380,6 +380,9 @@ class EdgeIndex using LocalEdgeListChunk = typename StorageT::LocalEdgeListChunk; struct EdgeListChunk { +#ifdef HAVE_HPX + EdgeListChunk() = default; +#endif EdgeListChunk(ObjectID &_oid, SrcT _src, LocalEdgeListChunk &_chunk) : oid(_oid), src(_src), chunk(_chunk) {} typename EdgeIndex::ObjectID oid; diff --git a/include/shad/extensions/graph_library/local_edge_index.h b/include/shad/extensions/graph_library/local_edge_index.h index 42a7b5e4..1fa87ec9 100755 --- a/include/shad/extensions/graph_library/local_edge_index.h +++ b/include/shad/extensions/graph_library/local_edge_index.h @@ -51,6 +51,9 @@ class DefaultEdgeIndexStorage { public: struct EmptyAttr {}; using SrcAttributesT = EmptyAttr; +#ifdef HAVE_HPX + DefaultEdgeIndexStorage() = default; +#endif explicit DefaultEdgeIndexStorage(const size_t numVertices) : edgeList_(std::max(numVertices / constants::kDefaultNumEntriesPerBucket, 1lu)) {} @@ -59,6 +62,9 @@ class DefaultEdgeIndexStorage { 1lu)) {} static constexpr size_t kEdgeListChunkSize_ = 3072 / sizeof(DestT); struct LocalEdgeListChunk { +#ifdef HAVE_HPX + LocalEdgeListChunk() = default; +#endif LocalEdgeListChunk(size_t _numDest, bool _ow, DestT* _dest) : numDest(_numDest), overwrite(_ow) { memcpy(destinations.data(), _dest, diff --git a/include/shad/runtime/handle.h b/include/shad/runtime/handle.h index 57224f18..44dab05e 100644 --- a/include/shad/runtime/handle.h +++ b/include/shad/runtime/handle.h @@ -31,6 +31,10 @@ #include "shad/runtime/mapping_traits.h" #include "shad/runtime/mappings/available_traits_mappings.h" +#ifdef HAVE_HPX +#include "hpx/serialization.hpp" +#endif + namespace shad { namespace rt { @@ -87,8 +91,24 @@ class Handle { /// @brief Null Test. /// @return true if the Handle is null, false otherwise. bool IsNull() const { +#ifdef HAVE_HPX + return impl::HandleTrait::Equal( + id_, impl::HandleTrait::NullValue()); +#else return id_ == impl::HandleTrait::NullValue(); +#endif + } + + #ifdef HAVE_HPX + private: + friend class hpx::serialization::access; + + template + void serialize(Archive& ar, unsigned) + { + ar & id_; } +#endif private: friend void waitForCompletion(Handle &handle); diff --git a/include/shad/runtime/mappings/available_mappings.h b/include/shad/runtime/mappings/available_mappings.h index d8eb5d87..e9a28859 100644 --- a/include/shad/runtime/mappings/available_mappings.h +++ b/include/shad/runtime/mappings/available_mappings.h @@ -34,6 +34,9 @@ #elif defined HAVE_GMT #include "shad/runtime/mappings/gmt/gmt_asynchronous_interface.h" #include "shad/runtime/mappings/gmt/gmt_synchronous_interface.h" +#elif defined HAVE_HPX +#include "shad/runtime/mappings/hpx/hpx_asynchronous_interface.h" +#include "shad/runtime/mappings/hpx/hpx_synchronous_interface.h" #else #error Unsupported Runtime System #endif diff --git a/include/shad/runtime/mappings/available_traits_mappings.h b/include/shad/runtime/mappings/available_traits_mappings.h index 21eb7949..63d2f086 100644 --- a/include/shad/runtime/mappings/available_traits_mappings.h +++ b/include/shad/runtime/mappings/available_traits_mappings.h @@ -31,6 +31,8 @@ #include "shad/runtime/mappings/tbb/tbb_traits_mapping.h" #elif defined HAVE_GMT #include "shad/runtime/mappings/gmt/gmt_traits_mapping.h" +#elif defined HAVE_HPX +#include "shad/runtime/mappings/hpx/hpx_traits_mapping.h" #endif #endif // INCLUDE_SHAD_RUNTIME_MAPPINGS_AVAILABLE_TRAITS_MAPPINGS_H_ diff --git a/include/shad/runtime/mappings/hpx/hpx_asynchronous_interface.h b/include/shad/runtime/mappings/hpx/hpx_asynchronous_interface.h new file mode 100644 index 00000000..faac2d3e --- /dev/null +++ b/include/shad/runtime/mappings/hpx/hpx_asynchronous_interface.h @@ -0,0 +1,390 @@ +//===------------------------------------------------------------*- C++ -*-===// +// +// SHAD +// +// The Scalable High-performance Algorithms and Data Structure Library +// +//===----------------------------------------------------------------------===// +// +// Copyright 2018 Battelle Memorial Institute +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +//===----------------------------------------------------------------------===// + +#ifndef INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_ASYNCHRONOUS_INTERFACE_H_ +#define INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_ASYNCHRONOUS_INTERFACE_H_ + +#include +#include +#include +#include + +#include "shad/runtime/asynchronous_interface.h" +#include "shad/runtime/handle.h" +#include "shad/runtime/locality.h" +#include "shad/runtime/mapping_traits.h" +#include "shad/runtime/mappings/hpx/hpx_traits_mapping.h" +#include "shad/runtime/mappings/hpx/hpx_utility.h" + +namespace shad { +namespace rt { + +namespace impl { + +template <> +struct AsynchronousInterface { + template + static void asyncExecuteAt(Handle &handle, const Locality &loc, + FunT &&function, const InArgsT &args) { + using FunctionTy = void (*)(Handle &, const InArgsT &); + + FunctionTy fn = std::forward(function); + + checkLocality(loc); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncExecuteAt_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + handle.id_->run( + [=]() { action_type()(id, fn, args); }); + } + + template + static void asyncExecuteAt(Handle &handle, const Locality &loc, + FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize) { + using FunctionTy = void (*)(Handle &, const uint8_t *, const uint32_t); + + FunctionTy fn = std::forward(function); + + checkLocality(loc); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncExecuteAt_buff_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + handle.id_->run([=]() { + action_type()( + id, fn, + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference)); + }); + + } + + template + static void asyncExecuteAtWithRetBuff(Handle &handle, const Locality &loc, + FunT &&function, const InArgsT &args, + uint8_t *resultBuffer, + uint32_t *resultSize) { + using FunctionTy = + void (*)(Handle &, const InArgsT &, uint8_t *, uint32_t *); + + FunctionTy fn = std::forward(function); + + checkLocality(loc); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncExecuteAtWithRetBuff_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + handle.id_->run([=]() { + buffer_type result = action_type()(id, fn, args, *resultSize); + + std::memcpy(resultBuffer, result.data(), result.size()); + *resultSize = result.size(); + }); + } + + template + static void asyncExecuteAtWithRetBuff( + Handle &handle, const Locality &loc, FunT &&function, + const std::shared_ptr &argsBuffer, const uint32_t bufferSize, + uint8_t *resultBuffer, uint32_t *resultSize) { + using FunctionTy = void (*)(Handle &, const uint8_t *, const uint32_t, + uint8_t *, uint32_t *); + + FunctionTy fn = std::forward(function); + + checkLocality(loc); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = + invoke_asyncExecuteAtWithRetBuff_buff_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + handle.id_->run([=]() { + buffer_type result = action_type()( + id, fn, + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference), + *resultSize); + + std::memcpy(resultBuffer, result.data(), result.size()); + *resultSize = result.size(); + }); + } + + template + static void asyncExecuteAtWithRet(Handle &handle, const Locality &loc, + FunT &&function, const InArgsT &args, + ResT *result) { + using FunctionTy = void (*)(Handle &, const InArgsT &, ResT *); + + FunctionTy fn = std::forward(function); + + checkLocality(loc); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncExecuteAtWithRet_action; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + handle.id_->run([=]() { + *result = action_type()(id, fn, args, *result); + }); + + } + + template + static void asyncExecuteAtWithRet(Handle &handle, const Locality &loc, + FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize, ResT *result) { + using FunctionTy = + void (*)(Handle &, const uint8_t *, const uint32_t, ResT *); + + FunctionTy fn = std::forward(function); + + checkLocality(loc); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncExecuteAtWithRet_buff_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + handle.id_->run([=]() { + *result = action_type()( + id, fn, + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference), + *result); + }); + } + + template + static void asyncExecuteOnAll(Handle &handle, FunT &&function, + const InArgsT &args) { + using FunctionTy = void (*)(Handle &, const InArgsT &); + + FunctionTy fn = std::forward(function); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncExecuteAt_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::vector localities = hpx::find_all_localities(); + std::vector> futures; + for (hpx::naming::id_type const &loc : localities) { + handle.id_->run([=]() { + action_type()(loc, fn, args); + }); + } + + } + + template + static void asyncExecuteOnAll(Handle &handle, FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize) { + using FunctionTy = void (*)(Handle &, const uint8_t *, const uint32_t); + + FunctionTy fn = std::forward(function); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncExecuteAt_buff_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::vector localities = hpx::find_all_localities(); + std::vector> futures; + for (hpx::naming::id_type const &loc : localities) { + handle.id_->run([=]() { + action_type()( + loc, fn, + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference)); + }); + } + + } + + template + static void asyncForEachAt(Handle &handle, const Locality &loc, + FunT &&function, const InArgsT &args, + const size_t numIters) { + using FunctionTy = void (*)(Handle &, const InArgsT &, size_t); + + FunctionTy fn = std::forward(function); + + checkLocality(loc); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncForEachAt_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + handle.id_->run([=](){ + action_type()(id, fn, args, numIters); + }); + + } + + template + static void asyncForEachAt(Handle &handle, const Locality &loc, + FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize, const size_t numIters) { + using FunctionTy = + void (*)(Handle &, const uint8_t *, const uint32_t, size_t); + + FunctionTy fn = std::forward(function); + + checkLocality(loc); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncForEachAt_buff_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + handle.id_->run([=]() { + action_type()( + id, fn, + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference), + numIters); + }); + } + + template + static void asyncForEachOnAll(Handle &handle, FunT &&function, + const InArgsT &args, const size_t numIters) { + using FunctionTy = void (*)(Handle &, const InArgsT &, size_t); + + FunctionTy fn = std::forward(function); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncForEachOnAll_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::vector localities = hpx::find_all_localities(); + std::size_t last_loc_idx = localities.size() - 1; + std::size_t iters = (numIters + last_loc_idx) / localities.size(); + std::size_t iters_last = numIters - iters * last_loc_idx; + + for (std::size_t i = 0; i != last_loc_idx; ++i) { + hpx::id_type const &loc = localities[i]; + handle.id_->run([=]() { + action_type()(loc, fn, args, iters * i, iters * (i + 1)); + }); + } + + hpx::id_type const &loc_last = localities[last_loc_idx]; + handle.id_->run([=]() { + action_type()(loc_last, fn, args, iters * last_loc_idx, numIters); + }); + } + + template + static void asyncForEachOnAll(Handle &handle, FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize, + const size_t numIters) { + using FunctionTy = + void (*)(Handle &, const uint8_t *, const uint32_t, size_t); + + FunctionTy fn = std::forward(function); + + handle.id_ = + handle.IsNull() ? HandleTrait::CreateNewHandle() : handle.id_; + + using action_type = invoke_asyncForEachOnAll_buff_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::vector localities = hpx::find_all_localities(); + std::size_t last_loc_idx = localities.size() - 1; + std::size_t iters = (numIters + last_loc_idx) / localities.size(); + std::size_t iters_last = numIters - iters * last_loc_idx; + + auto buffer = + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference); + + for (std::size_t i = 0; i != last_loc_idx; ++i) { + hpx::id_type const &loc = localities[i]; + handle.id_->run([=]() { + action_type()(loc, fn, buffer, iters * i, iters * (i + 1)); + }); + } + + hpx::id_type const &loc = localities[last_loc_idx]; + handle.id_->run([=]() { + action_type()(loc, fn, buffer, iters * last_loc_idx, numIters); + }); + } + +}; + +} // namespace impl + +} // namespace rt +} // namespace shad + +#endif // INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_ASYNCHRONOUS_INTERFACE_H_ diff --git a/include/shad/runtime/mappings/hpx/hpx_synchronous_interface.h b/include/shad/runtime/mappings/hpx/hpx_synchronous_interface.h new file mode 100644 index 00000000..a553974e --- /dev/null +++ b/include/shad/runtime/mappings/hpx/hpx_synchronous_interface.h @@ -0,0 +1,351 @@ +//===------------------------------------------------------------*- C++ -*-===// +// +// SHAD +// +// The Scalable High-performance Algorithms and Data Structure Library +// +//===----------------------------------------------------------------------===// +// +// Copyright 2018 Battelle Memorial Institute +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +//===----------------------------------------------------------------------===// + +#ifndef INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_SYNCHRONOUS_INTERFACE_H_ +#define INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_SYNCHRONOUS_INTERFACE_H_ + +#include +#include +#include +#include + +#include "hpx/hpx.hpp" +#include "hpx/modules/serialization.hpp" + +#include "shad/runtime/locality.h" +#include "shad/runtime/mappings/hpx/hpx_traits_mapping.h" +#include "shad/runtime/mappings/hpx/hpx_utility.h" +#include "shad/runtime/synchronous_interface.h" + +namespace shad { +namespace rt { + +namespace impl { + +template <> +struct SynchronousInterface { + template + static void executeAt(const Locality &loc, FunT &&function, + const InArgsT &args) { + using FunctionTy = void (*)(const InArgsT &); + + checkLocality(loc); + FunctionTy fn = std::forward(function); + + using action_type = invoke_executeAt_action; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + hpx::sync(id, fn, args); + } + + template + static void executeAt(const Locality &loc, FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize) { + using FunctionTy = void (*)(const uint8_t *, const uint32_t); + + FunctionTy fn = std::forward(function); + checkLocality(loc); + + using action_type = invoke_executeAt_buffer_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + hpx::sync( + id, fn, + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference)); + + } + + template + static void executeAtWithRetBuff(const Locality &loc, FunT &&function, + const InArgsT &args, uint8_t *resultBuffer, + uint32_t *resultSize) { + using FunctionTy = void (*)(const InArgsT &, uint8_t *, uint32_t *); + + FunctionTy fn = std::forward(function); + checkLocality(loc); + + using action_type = invoke_executeAtWithRetBuff_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + buffer_type result = hpx::sync( + id, fn, args, *resultSize); + + *resultSize = result.size(); + std::memcpy(resultBuffer, result.data(), result.size()); + } + + template + static void executeAtWithRetBuff(const Locality &loc, FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize, + uint8_t *resultBuffer, + uint32_t *resultSize) { + using FunctionTy = + void (*)(const uint8_t *, const uint32_t, uint8_t *, uint32_t *); + + FunctionTy fn = std::forward(function); + checkLocality(loc); + + using action_type = invoke_executeAtWithRetBuff_buff_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + buffer_type result = hpx::sync( + id, fn, + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference), + *resultSize); + + *resultSize = result.size(); + std::memcpy(resultBuffer, result.data(), result.size()); + } + + template + static void executeAtWithRet(const Locality &loc, FunT &&function, + const InArgsT &args, ResT *result) { + using FunctionTy = void (*)(const InArgsT &, ResT *); + + FunctionTy fn = std::forward(function); + checkLocality(loc); + + using action_type = invoke_executeAtWithRet_action; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + *result = hpx::sync(id, fn, args, *result); + } + + template + static void executeAtWithRet(const Locality &loc, FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize, ResT *result) { + using FunctionTy = void (*)(const uint8_t *, const uint32_t, ResT *); + + FunctionTy fn = std::forward(function); + checkLocality(loc); + + using action_type = invoke_executeAtWithRet_buff_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + *result = hpx::sync(id, fn, + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference), + *result); + } + + template + static void executeOnAll(FunT &&function, const InArgsT &args) { + using FunctionTy = void (*)(const InArgsT &); + + FunctionTy fn = std::forward(function); + + using action_type = invoke_executeAt_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::vector localities = hpx::find_all_localities(); + std::vector> futures; + for (hpx::naming::id_type const &loc : localities) { + futures.push_back(hpx::async( + loc, fn, args)); + } + hpx::wait_all(futures); + } + + template + static void executeOnAll(FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize) { + using FunctionTy = void (*)(const uint8_t *, const uint32_t); + + FunctionTy fn = std::forward(function); + + using action_type = invoke_executeAt_buffer_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::vector localities = hpx::find_all_localities(); + std::vector> futures; + for (hpx::naming::id_type const &loc : localities) { + futures.push_back(hpx::async( + loc, fn, + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference))); + } + hpx::wait_all(futures); + + } + + template + static void forEachAt(const Locality &loc, FunT &&function, + const InArgsT &args, const size_t numIters) { + using FunctionTy = void (*)(const InArgsT &, size_t); + + FunctionTy fn = std::forward(function); + + checkLocality(loc); + + using action_type = invoke_forEachAt_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + hpx::sync(id, fn, args, numIters); + } + + template + static void forEachAt(const Locality &loc, FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize, const size_t numIters) { + using FunctionTy = void (*)(const uint8_t *, const uint32_t, size_t); + + FunctionTy fn = std::forward(function); + + checkLocality(loc); + + using action_type = invoke_forEachAt_buffer_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(loc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + hpx::sync(id, fn, + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference), + numIters); + } + + template + static void forEachOnAll(FunT &&function, const InArgsT &args, + const size_t numIters) { + using FunctionTy = void (*)(const InArgsT &, size_t); + + FunctionTy fn = std::forward(function); + + using action_type = invoke_forEachOnAll_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::vector localities = hpx::find_all_localities(); + std::size_t last_loc_idx = localities.size() - 1; + std::size_t iters = (numIters + last_loc_idx) / localities.size(); + std::size_t iters_last = numIters - iters * last_loc_idx; + std::vector> futures; + + for (std::size_t i = 0; i != last_loc_idx; ++i) { + hpx::id_type const &loc = localities[i]; + futures.push_back( + hpx::async(loc, fn, args, iters * i, iters * (i + 1))); + } + + hpx::id_type const &loc = localities[last_loc_idx]; + futures.push_back( + hpx::async(loc, fn, args, iters * last_loc_idx, numIters)); + + hpx::wait_all(futures); + } + + template + static void forEachOnAll(FunT &&function, + const std::shared_ptr &argsBuffer, + const uint32_t bufferSize, const size_t numIters) { + using FunctionTy = void (*)(const uint8_t *, const uint32_t, size_t); + + FunctionTy fn = std::forward(function); + + using action_type = invoke_forEachOnAll_buffer_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::vector localities = hpx::find_all_localities(); + std::size_t last_loc_idx = localities.size() - 1; + std::size_t iters = (numIters + last_loc_idx) / localities.size(); + std::size_t iters_last = numIters - iters * last_loc_idx; + std::vector> futures; + + auto buffer = + buffer_type(argsBuffer.get(), bufferSize, buffer_type::reference); + + for (std::size_t i = 0; i != last_loc_idx; ++i) { + hpx::id_type const &loc = localities[i]; + futures.push_back( + hpx::async(loc, fn, buffer, iters * i, iters * (i + 1))); + } + + hpx::id_type const &loc = localities[last_loc_idx]; + futures.push_back( + hpx::async(loc, fn, buffer, iters * last_loc_idx, numIters)); + + hpx::wait_all(futures); + } + + template + static void dma(const Locality &destLoc, const T *remoteAddress, + const T *localData, const size_t numElements) { + + using action_type = invoke_dma_put_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(destLoc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + hpx::sync( + id, + buffer_type(reinterpret_cast(localData), + numElements * sizeof(T), buffer_type::reference), + reinterpret_cast(remoteAddress)); + } + + template + static void dma(const T *localAddress, const Locality &srcLoc, + const T *remoteData, const size_t numElements) { + + using action_type = invoke_dma_get_action; + using buffer_type = hpx::serialization::serialize_buffer; + + std::uint32_t loc_id = getLocalityId(srcLoc); + hpx::naming::id_type id = hpx::naming::get_id_from_locality_id(loc_id); + + buffer_type res = hpx::sync( + id, reinterpret_cast(remoteData), numElements * sizeof(T)); + + std::memcpy( + const_cast(reinterpret_cast(localAddress)), + res.data(), res.size()); + } +}; + +} // namespace impl + +} // namespace rt +} // namespace shad + +#endif // INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_SYNCHRONOUS_INTERFACE_H_ diff --git a/include/shad/runtime/mappings/hpx/hpx_traits_mapping.h b/include/shad/runtime/mappings/hpx/hpx_traits_mapping.h new file mode 100644 index 00000000..9ff6cb10 --- /dev/null +++ b/include/shad/runtime/mappings/hpx/hpx_traits_mapping.h @@ -0,0 +1,108 @@ +//===------------------------------------------------------------*- C++ -*-===// +// +// SHAD +// +// The Scalable High-performance Algorithms and Data Structure Library +// +//===----------------------------------------------------------------------===// +// +// Copyright 2018 Battelle Memorial Institute +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +//===----------------------------------------------------------------------===// + +#ifndef INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_TRAITS_MAPPING_H_ +#define INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_TRAITS_MAPPING_H_ + +#include +#include +#include +#include + +#include "hpx/hpx.hpp" + +#include "shad/runtime/mapping_traits.h" + +namespace shad { + +namespace rt { +namespace impl { + +struct hpx_tag {}; +template <> +struct HandleTrait { + using HandleTy = std::shared_ptr; + using ParameterTy = std::shared_ptr &; + using ConstParameterTy = const std::shared_ptr &; + + static void Init(ParameterTy H, ConstParameterTy V) {H=V;} + + static HandleTy NullValue() { + return std::shared_ptr(nullptr); + } + + static bool Equal(ConstParameterTy lhs, ConstParameterTy rhs) { + return lhs == rhs; + } + + static std::string toString(ConstParameterTy H) { return ""; } + + static uint64_t toUnsignedInt(ConstParameterTy H) { + return reinterpret_cast(H.get()); + } + + static HandleTy CreateNewHandle() { + return std::shared_ptr(new hpx::execution::experimental::task_group()); + } + + static void WaitFor(ParameterTy H) { + if (H == nullptr){ + return; + } + H->wait(); + } +}; + +template <> +struct LockTrait { + using LockTy = hpx::lcos::local::spinlock; + + static void lock(LockTy &L) { L.lock(); hpx::util::ignore_lock(&L); } + static void unlock(LockTy &L) { hpx::util::reset_ignored(&L); L.unlock(); } +}; + +template <> +struct RuntimeInternalsTrait { + static void Initialize(int argc, char *argv[]) {} + + static void Finalize() {} + + static size_t Concurrency() { return hpx::get_os_thread_count(); } + static void Yield() { hpx::this_thread::yield(); } + + static uint32_t ThisLocality() { return hpx::get_locality_id(); } + static uint32_t NullLocality() { return hpx::naming::invalid_locality_id; } + static uint32_t NumLocalities(){ + return hpx::get_num_localities(hpx::launch::sync); + } +}; + +} // namespace impl + +using TargetSystemTag = impl::hpx_tag; + +} // namespace rt +} // namespace shad + +#endif // INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_TRAITS_MAPPING_H_ diff --git a/include/shad/runtime/mappings/hpx/hpx_utility.h b/include/shad/runtime/mappings/hpx/hpx_utility.h new file mode 100644 index 00000000..5718fa96 --- /dev/null +++ b/include/shad/runtime/mappings/hpx/hpx_utility.h @@ -0,0 +1,628 @@ +//===------------------------------------------------------------*- C++ -*-===// +// +// SHAD +// +// The Scalable High-performance Algorithms and Data Structure Library +// +//===----------------------------------------------------------------------===// +// +// Copyright 2018 Battelle Memorial Institute +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +//===----------------------------------------------------------------------===// + +#ifndef INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_UTILITY_H_ +#define INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_UTILITY_H_ + +#include +#include +#include +#include +#include + +#include "hpx/hpx.hpp" +#include "hpx/hpx_init.hpp" +#include "hpx/serialization/serialize_buffer.hpp" + +#include "shad/runtime/locality.h" + +namespace shad { +namespace rt { + +namespace impl { + +inline uint32_t getLocalityId(const Locality &loc) { + return static_cast(loc); +} + +inline void checkLocality(const Locality& loc) { + uint32_t localityID = getLocalityId(loc); + if (localityID >= hpx::get_num_localities(hpx::launch::sync)) { + std::stringstream ss; + ss << "The system does not include " << loc; + throw std::system_error(0xdeadc0de, std::generic_category(), ss.str()); + } +} + +namespace detail { +/////////////////////////////////////////////////////////////////////// +// simple utility action which invoke an arbitrary global function +// sync functions +template +struct invoke_executeAt; +template +struct invoke_executeAt { + static void call(void (*f)(T), T args) { return f(std::move(args)); } +}; + +struct invoke_executeAt_buffer { + static void call(void (*f)(const uint8_t *, const uint32_t), + hpx::serialization::serialize_buffer args) { + f(args.data(), args.size()); + } +}; + +template +struct invoke_executeAtWithRetBuff; +template +struct invoke_executeAtWithRetBuff { + static hpx::serialization::serialize_buffer call( + void (*f)(T, std::uint8_t *, std::uint32_t *), T args, + std::uint32_t size) { + hpx::serialization::serialize_buffer result(2048); + + f(std::move(args), result.data(), &size); + + result.resize_norealloc(size); + return result; + } +}; + +struct invoke_executeAtWithRetBuff_buff { + static hpx::serialization::serialize_buffer call( + void (*f)(const uint8_t *, const uint32_t, uint8_t *, uint32_t *), + hpx::serialization::serialize_buffer args, + std::uint32_t size) { + hpx::serialization::serialize_buffer result(2048); + f(args.data(), args.size(), result.data(), &size); + result.resize_norealloc(size); + return result; + } +}; + +template +struct invoke_executeAtWithRet; +template +struct invoke_executeAtWithRet { + static R call(void (*f)(T, R *), T args, R result) { + f(std::move(args), &result); + + return result; + } +}; + +template +struct invoke_executeAtWithRet_buff; +template +struct invoke_executeAtWithRet_buff { + static R call(void (*f)(const uint8_t *, const uint32_t, R *), + hpx::serialization::serialize_buffer args, + R result) { + f(args.data(), args.size(), &result); + + return result; + } +}; + +template +struct invoke_forEachAt; +template +struct invoke_forEachAt { + static void call(void (*f)(T, std::size_t), T args, std::size_t numIters) { + hpx::for_loop(hpx::execution::par, 0, numIters, [&](std::size_t i) { + reinterpret_cast(f)(std::move(args), i); + }); + } +}; + +struct invoke_forEachAt_buffer { + static void call(void (*f)(const uint8_t *, const uint32_t, size_t), + hpx::serialization::serialize_buffer args, + std::size_t numIters) { + hpx::for_loop(hpx::execution::par, 0, numIters, + [&](std::size_t i) { f(args.data(), args.size(), i); }); + } +}; + +template +struct invoke_forEachOnAll; +template +struct invoke_forEachOnAll { + static void call(void (*f)(T, std::size_t), T args, std::size_t beginIter, + std::size_t endIter) { + hpx::for_loop(hpx::execution::par, beginIter, endIter, [&](std::size_t i) { + f(std::move(args), i); + }); + } +}; + +struct invoke_forEachOnAll_buffer { + static void call(void (*f)(const uint8_t *, const uint32_t, size_t), + hpx::serialization::serialize_buffer args, + std::size_t beginIter, std::size_t endIter) { + hpx::for_loop(hpx::execution::par, beginIter, endIter, [&](std::size_t i) { + f(args.data(), args.size(), i); + }); + } +}; + +template +struct invoke_dma_put { + static void call(hpx::serialization::serialize_buffer args, + std::size_t remoteAddress) { + std::memcpy(reinterpret_cast(remoteAddress), args.data(), args.size()); + } +}; + +struct invoke_dma_get { + static hpx::serialization::serialize_buffer call( + std::size_t remoteData, std::size_t numBytes) { + hpx::serialization::serialize_buffer result( + reinterpret_cast(remoteData), numBytes, + hpx::serialization::serialize_buffer::reference); + return result; + } +}; + +////////////////////////////////////////////////////////////////////////////// +// async functions +template +struct invoke_asyncExecuteAt; +template +struct invoke_asyncExecuteAt { + static void call(void (*f)(H, T), T args) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + + f(h, std::move(args)); + + waitForCompletion(h); + } +}; + +template +struct invoke_asyncExecuteAt_buff; +template +struct invoke_asyncExecuteAt_buff { + static void call(void (*f)(H, const uint8_t *, const uint32_t), + hpx::serialization::serialize_buffer args) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + + f(h, args.data(), args.size()); + + waitForCompletion(h); + } +}; + +template +struct invoke_asyncExecuteAtWithRetBuff; +template +struct invoke_asyncExecuteAtWithRetBuff { + static hpx::serialization::serialize_buffer call( + void (*f)(H, T, uint8_t *, uint32_t *), T args, std::uint32_t size) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + + hpx::serialization::serialize_buffer result(2048); + + f(h, std::move(args), result.data(), &size); + + waitForCompletion(h); + result.resize_norealloc(size); + return result; + } +}; + +template +struct invoke_asyncExecuteAtWithRetBuff_buff; +template +struct invoke_asyncExecuteAtWithRetBuff_buff { + static hpx::serialization::serialize_buffer call( + void (*f)(H, const uint8_t *, const uint32_t, uint8_t *, uint32_t *), + hpx::serialization::serialize_buffer args, + std::uint32_t size) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + + hpx::serialization::serialize_buffer result(2048); + + f(h, args.data(), args.size(), result.data(), &size); + + waitForCompletion(h); + result.resize_norealloc(size); + return result; + } +}; + +template +struct invoke_asyncExecuteAtWithRet; +template +struct invoke_asyncExecuteAtWithRet { + static R call(void (*f)(H, T, R *), T args, R result) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + + f(h, std::move(args), &result); + + waitForCompletion(h); + + return result; + } +}; + +template +struct invoke_asyncExecuteAtWithRet_buff; +template +struct invoke_asyncExecuteAtWithRet_buff { + static R call(void (*f)(H, const uint8_t *, const uint32_t, R *), + hpx::serialization::serialize_buffer args, + R result) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + + f(h, args.data(), args.size(), &result); + + waitForCompletion(h); + return result; + } +}; + +template +struct invoke_asyncForEachAt; +template +struct invoke_asyncForEachAt { + static void call(void (*f)(H, T, std::size_t), T args, std::size_t numIters) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + hpx::for_loop(hpx::execution::par, 0, numIters, + [&](std::size_t i) { f(h, std::move(args), i); }); + waitForCompletion(h); + } +}; + +template +struct invoke_asyncForEachAt_buff; +template +struct invoke_asyncForEachAt_buff { + static void call(void (*f)(H, const uint8_t *, const uint32_t, std::size_t), + hpx::serialization::serialize_buffer args, + std::size_t numIters) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + hpx::for_loop(hpx::execution::par, 0, numIters, + [&](std::size_t i) { f(h, args.data(), args.size(), i); }); + waitForCompletion(h); + } +}; + +template +struct invoke_asyncForEachOnAll; +template +struct invoke_asyncForEachOnAll { + static void call(void (*f)(H, T, std::size_t), T args, std::size_t beginIter, + std::size_t endIter) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + hpx::for_loop(hpx::execution::par, beginIter, endIter, + [&](std::size_t i) { f(h, std::move(args), i); }); + waitForCompletion(h); + } +}; + +template +struct invoke_asyncForEachOnAll_buff; +template +struct invoke_asyncForEachOnAll_buff { + static void call(void (*f)(H, const uint8_t *, const uint32_t, std::size_t), + hpx::serialization::serialize_buffer args, + std::size_t beginIter, std::size_t endIter) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + hpx::for_loop(hpx::execution::par, beginIter, endIter, + [&](std::size_t i) { f(h, args.data(), args.size(), i); }); + waitForCompletion(h); + } +}; + +template +struct invoke_asyncDma_put { + static void call(hpx::serialization::serialize_buffer args, + std::size_t remoteAddress) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + + std::memcpy(reinterpret_cast(remoteAddress), args.data(), args.size()); + + waitForCompletion(h); + } +}; + +template +struct invoke_asyncDma_get { + static hpx::serialization::serialize_buffer call( + std::size_t remoteData, std::size_t numBytes) { + std::remove_reference_t h(HandleTrait::CreateNewHandle()); + + hpx::serialization::serialize_buffer result( + reinterpret_cast(remoteData), numBytes, + hpx::serialization::serialize_buffer::reference); + + waitForCompletion(h); + return result; + } +}; + +} // namespace detail + +// action definition exposing invoke_function_ptr<> that binds a global +// function (Note: this assumes global function addresses are the same on +// all localities. This also assumes that all argument types are bitwise +// copyable + +// sync actions +template +struct invoke_executeAt_action; +template +struct invoke_executeAt_action + : ::hpx::actions::action::call, + invoke_executeAt_action> {}; + +struct invoke_executeAt_buffer_action + : ::hpx::actions::action< + void (*)(void (*)(const uint8_t *, const uint32_t), + hpx::serialization::serialize_buffer), + &detail::invoke_executeAt_buffer::call, + invoke_executeAt_buffer_action> {}; + +template +struct invoke_executeAtWithRetBuff_action; +template +struct invoke_executeAtWithRetBuff_action + : ::hpx::actions::action< + hpx::serialization::serialize_buffer (*)( + void (*)(T, std::uint8_t *, std::uint32_t *), T, std::uint32_t), + &detail::invoke_executeAtWithRetBuff::call, + invoke_executeAtWithRetBuff_action> {}; + +struct invoke_executeAtWithRetBuff_buff_action + : ::hpx::actions::action< + hpx::serialization::serialize_buffer (*)( + void (*)(const uint8_t *, const uint32_t, uint8_t *, uint32_t *), + hpx::serialization::serialize_buffer, + std::uint32_t), + &detail::invoke_executeAtWithRetBuff_buff::call, + invoke_executeAtWithRetBuff_buff_action> {}; + +template +struct invoke_executeAtWithRet_action; +template +struct invoke_executeAtWithRet_action + : ::hpx::actions::action< + R (*)(void (*)(T, R *), T, R), + &detail::invoke_executeAtWithRet::call, + invoke_executeAtWithRet_action> {}; + +template +struct invoke_executeAtWithRet_buff_action; +template +struct invoke_executeAtWithRet_buff_action + : ::hpx::actions::action< + R (*)(void (*)(const uint8_t *, const uint32_t, R *), + hpx::serialization::serialize_buffer, R), + &detail::invoke_executeAtWithRet_buff::call, + invoke_executeAtWithRet_buff_action> {}; + +template +struct invoke_forEachAt_action; +template +struct invoke_forEachAt_action + : ::hpx::actions::action< + void (*)(void (*)(T, std::size_t), T, std::size_t), + &detail::invoke_forEachAt::call, + invoke_forEachAt_action> {}; + +struct invoke_forEachAt_buffer_action + : ::hpx::actions::action< + void (*)(void (*)(const uint8_t *, const uint32_t, size_t), + hpx::serialization::serialize_buffer, + std::size_t), + &detail::invoke_forEachAt_buffer::call, + invoke_forEachAt_buffer_action> {}; + +template +struct invoke_forEachOnAll_action; +template +struct invoke_forEachOnAll_action + : ::hpx::actions::action< + void (*)(void (*)(T, std::size_t), T, std::size_t, std::size_t), + &detail::invoke_forEachOnAll::call, + invoke_forEachOnAll_action> {}; + +struct invoke_forEachOnAll_buffer_action + : ::hpx::actions::action< + void (*)(void (*)(const uint8_t *, const uint32_t, size_t), + hpx::serialization::serialize_buffer, + std::size_t, std::size_t), + &detail::invoke_forEachOnAll_buffer::call, + invoke_forEachOnAll_buffer_action> {}; + +template +struct invoke_dma_put_action + : ::hpx::actions::action< + void (*)(hpx::serialization::serialize_buffer, + std::size_t), + &detail::invoke_dma_put::call, invoke_dma_put_action> {}; + +struct invoke_dma_get_action + : ::hpx::actions::action< + hpx::serialization::serialize_buffer (*)(std::size_t, + std::size_t), + &detail::invoke_dma_get::call, invoke_dma_get_action> {}; + +////////////////////////////////////////////////////////////////////////////// +// async actions +template +struct invoke_asyncExecuteAt_action; +template +struct invoke_asyncExecuteAt_action + : ::hpx::actions::action< + void (*)(void (*)(H, T), T), + &detail::invoke_asyncExecuteAt::call, + invoke_asyncExecuteAt_action> {}; + +template +struct invoke_asyncExecuteAt_buff_action; +template +struct invoke_asyncExecuteAt_buff_action + : ::hpx::actions::action< + void (*)(void (*)(H, const uint8_t *, const uint32_t), + hpx::serialization::serialize_buffer), + &detail::invoke_asyncExecuteAt_buff::call, + invoke_asyncExecuteAt_buff_action> {}; + +template +struct invoke_asyncExecuteAtWithRetBuff_action; +template +struct invoke_asyncExecuteAtWithRetBuff_action + : ::hpx::actions::action< + hpx::serialization::serialize_buffer (*)( + void (*)(H, T, uint8_t *, uint32_t *), T, std::uint32_t), + &detail::invoke_asyncExecuteAtWithRetBuff::call, + invoke_asyncExecuteAtWithRetBuff_action> {}; + +template +struct invoke_asyncExecuteAtWithRetBuff_buff_action; +template +struct invoke_asyncExecuteAtWithRetBuff_buff_action + : ::hpx::actions::action< + hpx::serialization::serialize_buffer (*)( + void (*)(H, const uint8_t *, const uint32_t, uint8_t *, + uint32_t *), + hpx::serialization::serialize_buffer, + std::uint32_t), + &detail::invoke_asyncExecuteAtWithRetBuff_buff::call, + invoke_asyncExecuteAtWithRetBuff_buff_action> {}; + +template +struct invoke_asyncExecuteAtWithRet_action; +template +struct invoke_asyncExecuteAtWithRet_action + : ::hpx::actions::action< + R (*)(void (*)(H, T, R *), T, R), + &detail::invoke_asyncExecuteAtWithRet::call, + invoke_asyncExecuteAtWithRet_action> {}; + +template +struct invoke_asyncExecuteAtWithRet_buff_action; +template +struct invoke_asyncExecuteAtWithRet_buff_action + : ::hpx::actions::action< + R (*)(void (*)(H, const uint8_t *, const uint32_t, R *), + hpx::serialization::serialize_buffer, R), + &detail::invoke_asyncExecuteAtWithRet_buff::call, + invoke_asyncExecuteAtWithRet_buff_action> {}; + +template +struct invoke_asyncForEachAt_action; +template +struct invoke_asyncForEachAt_action + : ::hpx::actions::action< + void (*)(void (*)(H, T, std::size_t), T, std::size_t), + &detail::invoke_asyncForEachAt::call, + invoke_asyncForEachAt_action> {}; + +template +struct invoke_asyncForEachAt_buff_action; +template +struct invoke_asyncForEachAt_buff_action + : ::hpx::actions::action< + void (*)(void (*)(H, const uint8_t *, const uint32_t, std::size_t), + hpx::serialization::serialize_buffer, + std::size_t), + &detail::invoke_asyncForEachAt_buff::call, + invoke_asyncForEachAt_buff_action> {}; + +template +struct invoke_asyncForEachOnAll_action; +template +struct invoke_asyncForEachOnAll_action + : ::hpx::actions::action< + void (*)(void (*)(H, T, std::size_t), T, std::size_t, std::size_t), + &detail::invoke_asyncForEachOnAll::call, + invoke_asyncForEachOnAll_action> {}; + +template +struct invoke_asyncForEachOnAll_buff_action; +template +struct invoke_asyncForEachOnAll_buff_action + : ::hpx::actions::action< + void (*)(void (*)(H, const uint8_t *, const uint32_t, std::size_t), + hpx::serialization::serialize_buffer, + std::size_t, std::size_t), + &detail::invoke_asyncForEachOnAll_buff::call, + invoke_asyncForEachOnAll_buff_action> {}; + +template +struct invoke_asyncDma_put_action + : ::hpx::actions::action< + void (*)(hpx::serialization::serialize_buffer, + std::size_t), + &detail::invoke_asyncDma_put::call, + invoke_asyncDma_put_action> {}; + +template +struct invoke_asyncDma_get_action + : ::hpx::actions::action (*)(std::size_t, std::size_t), + &detail::invoke_asyncDma_get::call, + invoke_asyncDma_get_action> {}; + +} // namespace impl + +} // namespace rt +} // namespace shad + +#endif // INCLUDE_SHAD_RUNTIME_MAPPINGS_HPX_HPX_UTILITY_H_ diff --git a/include/shad/runtime/runtime.h b/include/shad/runtime/runtime.h index 8d595e31..9d76762b 100644 --- a/include/shad/runtime/runtime.h +++ b/include/shad/runtime/runtime.h @@ -49,6 +49,36 @@ namespace shad { namespace rt { +/// @brief lambda_wrapper. +/// +/// The lambda_wrapper can be used to wrap lambda function. Three assumptions +/// are made. First, it is assumed taht the compiler will represent the lambda +/// function properly, for example, we could use sizeof(F) to decide the length +/// of byte. Second, it is assumed that the bytes buffer can work as the lambda +/// by reinterpret_cast<*F>. Third, it is assumed that this method only works +/// for capture with bitwise copyable types. For example, if capture a +/// std::string, it will not work. + +template +struct lambda_wrapper { + lambda_wrapper() = default; + + lambda_wrapper(F const &f) { + std::memcpy(buffer, reinterpret_cast(&f), sizeof(F)); + } + + template + auto operator()(Ts &&...ts) { + return (*reinterpret_cast(buffer))(std::forward(ts)...); + } + + template + auto operator()(Ts &&...ts) const { + return (*reinterpret_cast(buffer))(std::forward(ts)...); + } + + std::uint8_t buffer[sizeof(F)]; +}; /// @brief Lock. /// diff --git a/src/runtime/CMakeLists.txt b/src/runtime/CMakeLists.txt index 26b00f15..1554ae67 100644 --- a/src/runtime/CMakeLists.txt +++ b/src/runtime/CMakeLists.txt @@ -10,6 +10,10 @@ if (GMT_ROOT) set(runtime_prefixes ${runtime_prefixes} gmt) set(gmt_sources gmt_mapping/gmt_main.cc) endif() +if (HPX_ROOT) + set(runtime_prefixes ${runtime_prefixes} hpx) + set(hpx_sources hpx_mapping/hpx_main.cc) +endif() if (HAVE_CPP_SIMPLE) set(sources @@ -20,6 +24,9 @@ elseif (HAVE_TBB) elseif (HAVE_GMT) set(sources gmt_mapping/gmt_main.cc) +elseif (HAVE_HPX) + set(sources + hpx_mapping/hpx_main.cc) endif() @@ -50,7 +57,11 @@ foreach(rp ${runtime_prefixes}) ${${uprp}_INCLUDE_DIRS} $ $) - target_link_libraries(${rp}_runtime PUBLIC ${${uprp}_LIBRARIES}) + if (HAVE_HPX) + target_link_libraries(${rp}_runtime PUBLIC HPX::hpx) + else() + target_link_libraries(${rp}_runtime PUBLIC ${${uprp}_LIBRARIES}) + endif() target_compile_definitions(${rp}_runtime PUBLIC HAVE_${uprp}=1) install(TARGETS ${rp}_runtime EXPORT ${rp}_runtime_export @@ -75,5 +86,9 @@ elseif (HAVE_GMT) target_include_directories(runtime PUBLIC ${GMT_INCLUDE_DIRS}) target_compile_definitions(runtime PUBLIC HAVE_GMT=1) target_link_libraries(runtime PUBLIC ${GMT_LIBRARIES}) +elseif (HAVE_HPX) + target_include_directories(runtime PUBLIC ${HPX_INCLUDE_DIRS}) + target_compile_definitions(runtime PUBLIC HAVE_HPX=1) + target_link_libraries(runtime PUBLIC HPX::hpx) endif() install(TARGETS runtime ARCHIVE DESTINATION lib) diff --git a/src/runtime/hpx_mapping/hpx_main.cc b/src/runtime/hpx_mapping/hpx_main.cc new file mode 100644 index 00000000..846f6a59 --- /dev/null +++ b/src/runtime/hpx_mapping/hpx_main.cc @@ -0,0 +1,41 @@ +//===------------------------------------------------------------*- C++ -*-===// +// +// SHAD +// +// The Scalable High-performance Algorithms and Data Structure Library +// +//===----------------------------------------------------------------------===// +// +// Copyright 2018 Battelle Memorial Institute +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +// +//===----------------------------------------------------------------------===// + +#include +namespace shad { + +extern int main(int argc, char *argv[]); + +} // namespace shad + +int hpx_main(int argc, char *argv[]) { + int result = shad::main(argc, argv); + hpx::finalize(); + return result; +} + +int main(int argc, char *argv[]) { + return hpx::init(argc, argv); +} + diff --git a/test/unit_tests/core/shad_algorithm_test.cc b/test/unit_tests/core/shad_algorithm_test.cc index e0a114a6..52a6521d 100644 --- a/test/unit_tests/core/shad_algorithm_test.cc +++ b/test/unit_tests/core/shad_algorithm_test.cc @@ -431,13 +431,23 @@ TYPED_TEST(ATF, shad_generate) { auto flip_f = [n = 42]() mutable { return n = std::negate{}(n); }; this->test_void_with_policy( shad::distributed_sequential_tag{}, +#ifdef HAVE_HPX + shad::generate>, +#else shad::generate, +#endif shad_test_stl::generate_, shad_test_stl::ordered_checksum, flip_f); auto const_f = [n = 42]() mutable { return n; }; this->test_void_with_policy( shad::distributed_parallel_tag{}, +#ifdef HAVE_HPX + shad::generate>, +#else shad::generate, +#endif shad_test_stl::generate_, shad_test_stl::ordered_checksum, const_f); } @@ -466,14 +476,24 @@ TYPED_TEST(ATF, shad_replace_if) { auto pred = [](int x) { return (x % 3 == 0); }; this->test_void_with_policy( shad::distributed_sequential_tag{}, +#ifdef HAVE_HPX + shad::replace_if, val_t>, +#else shad::replace_if, +#endif shad_test_stl::replace_if_, shad_test_stl::ordered_checksum, pred, 3); this->test_void_with_policy( shad::distributed_parallel_tag{}, +#ifdef HAVE_HPX + shad::replace_if, val_t>, +#else shad::replace_if, +#endif shad_test_stl::replace_if_, shad_test_stl::ordered_checksum, pred, 3); } diff --git a/test/unit_tests/core/shad_numeric_test.cc b/test/unit_tests/core/shad_numeric_test.cc index efc19da2..9a9b1e26 100644 --- a/test/unit_tests/core/shad_numeric_test.cc +++ b/test/unit_tests/core/shad_numeric_test.cc @@ -299,8 +299,16 @@ TYPED_TEST(MTF, accumulate) { auto op_t = [](int64_t acc, const val_t &p) { return acc + shad_test_stl::to_int64{}(p); }; +#ifdef HAVE_HPX + this->test( + shad::accumulate>, + shad_test_stl::accumulate_>, + 0, op_t); +#else this->test(shad::accumulate, shad_test_stl::accumulate_, 0, op_t); +#endif } TYPED_TEST(MTF, transform_reduce_one_container) { diff --git a/test/unit_tests/data_structures/atomic_test.cc b/test/unit_tests/data_structures/atomic_test.cc index e5282cfa..8ec5da7c 100644 --- a/test/unit_tests/data_structures/atomic_test.cc +++ b/test/unit_tests/data_structures/atomic_test.cc @@ -266,11 +266,17 @@ TEST_F(AtomicTest, ForceFetchStore) { int64_t cnt = 0; for (auto loc : locs) { ptrs[cnt] = shad::Atomic::Create(kInitValue+cnt, loc); - results[cnt] = ptrs[cnt]->ForceFetchStore(cnt, - [](auto& a, auto& b) {return std::min(a, b);}); + results[cnt] = ptrs[cnt]->ForceFetchStore( + cnt, +#ifdef HAVE_HPX + shad::rt::lambda_wrapper( + [](auto& a, auto& b) { return std::min(a, b); })); +#else + [](auto& a, auto& b) { return std::min(a, b); }); +#endif ++cnt; } - + shad::rt::Handle h; cnt = 0; std::vector values(locs.size()); @@ -286,6 +292,7 @@ TEST_F(AtomicTest, ForceFetchStore) { ++cnt; } destroy(ptrs); + } TEST_F(AtomicTest, AsyncForceFetchStore) { @@ -296,9 +303,15 @@ TEST_F(AtomicTest, AsyncForceFetchStore) { shad::rt::Handle h; for (auto loc : locs) { ptrs[cnt] = shad::Atomic::Create(cnt, loc); - ptrs[cnt]->AsyncForceFetchStore(h, kInitValue + cnt, - [](auto& a, auto& b) {return std::max(a, b);}, - &results[cnt]); + ptrs[cnt]->AsyncForceFetchStore( + h, kInitValue + cnt, +#ifdef HAVE_HPX + shad::rt::lambda_wrapper( + [](auto& a, auto& b) { return std::max(a, b); }), +#else + [](auto& a, auto& b) { return std::max(a, b); }, +#endif + &results[cnt]); ++cnt; } shad::rt::waitForCompletion(h); diff --git a/test/unit_tests/runtime/CMakeLists.txt b/test/unit_tests/runtime/CMakeLists.txt index b310df23..ec665d6e 100644 --- a/test/unit_tests/runtime/CMakeLists.txt +++ b/test/unit_tests/runtime/CMakeLists.txt @@ -7,6 +7,10 @@ foreach(t ${tests}) ${t} PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}") endif() - target_link_libraries(${t} ${SHAD_RUNTIME_LIB} runtime shadtest_main) + if (HAVE_HPX) + target_link_libraries(${t} HPX::hpx HPX::iostreams_component runtime shadtest_main) + else() + target_link_libraries(${t} ${SHAD_RUNTIME_LIB} runtime shadtest_main) + endif() add_test(NAME ${t} COMMAND ${SHAD_TEST_COMMAND} $) endforeach(t)