From 52c293774d5358b3eb2faf2f3b6899e946decacc Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Wed, 28 Feb 2024 11:18:25 -0500 Subject: [PATCH 01/12] [serialization] introduced `byte_{i,o}streambuf`, to replace boost::iostreams::basic_array_sink --- ttg/ttg/serialization/stream.h | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/ttg/ttg/serialization/stream.h b/ttg/ttg/serialization/stream.h index c26781a53..9c50e0f83 100644 --- a/ttg/ttg/serialization/stream.h +++ b/ttg/ttg/serialization/stream.h @@ -6,6 +6,7 @@ #define TTG_SERIALIZATION_STREAM_H #include +#include namespace ttg::detail { @@ -66,6 +67,46 @@ namespace ttg::detail { const std::vector>& iovec_; }; + /// streambuf that writes bytes to a buffer in memory + class byte_ostreambuf : public std::streambuf { + public: + using std::streambuf::streambuf; + + byte_ostreambuf(char_type* buffer, std::streamsize buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} + + std::streamsize xsputn(const char_type* s, std::streamsize n) override { + assert((cursor_ - buffer_) + n <= buffer_size_); + std::memcpy(cursor_, s, n * sizeof(char_type)); + cursor_ += n; + return n; + } + + private: + char_type* buffer_; + char_type* cursor_; // current location in buffer_ + std::streamsize buffer_size_; + }; + + /// streambuf that writes bytes to a buffer in memory + class byte_istreambuf : public std::streambuf { + public: + using std::streambuf::streambuf; + + byte_istreambuf(char_type* buffer, std::size_t buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} + + std::streamsize xsgetn(char_type* s, std::streamsize max_n) override { + const auto n_to_read = std::min(buffer_size_ - (cursor_ - buffer_), max_n); + std::memcpy(s, cursor_, n_to_read * sizeof(char_type)); + cursor_ += n_to_read; + return n_to_read; + } + + private: + char_type* buffer_; + char_type* cursor_; // current location in buffer_ + std::streamsize buffer_size_; + }; + } // namespace ttg::detail #endif // TTG_SERIALIZATION_STREAM_H From 50251003dd7f4075ed3107170c4148581d42d0b7 Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Wed, 28 Feb 2024 11:21:44 -0500 Subject: [PATCH 02/12] [serialization] `boost_optimized_oarchive` moar optimized can call S::xsputn directly if S is derived from std::streambuf (i.e. is a streambuf) ... this permits inlining it --- .../serialization/backends/boost/archive.h | 76 +++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/ttg/ttg/serialization/backends/boost/archive.h b/ttg/ttg/serialization/backends/boost/archive.h index f9cebdace..ce7626e34 100644 --- a/ttg/ttg/serialization/backends/boost/archive.h +++ b/ttg/ttg/serialization/backends/boost/archive.h @@ -74,7 +74,19 @@ namespace ttg::detail { /// optimized data-only serializer - /// skips metadata (class version, etc.) + /// skips metadata (class version, etc.) by providing optimized save_override function that will be called by + /// `boost::archive::binary_oarchive_impl::save_override` + /// + /// \internal Using `boost::archive::binary_oarchive_impl` provides stock implementation for this custom archive. Unfortunately + /// `boost::archive::binary_oarchive_impl` uses the streambuf object via its std::streambuf base which means that + /// calls to `xsputn` are not inlined. To work around this problem, this class replaces several functions in + /// `boost::archive::binary_oarchive_impl` that were provided by `boost::archive::basic_binary_oprimitive` + /// such as `save`, `save_array` and `save_binary`; the latter calls the streambuf's sputn directly, + /// not via std::streambuf::sputn . To make sure these "replacements" are called this class must be used directly + /// rather than cast to `boost::archive::binary_oarchive_impl`, as an argument to + /// `oarchive_save_override_optimized_dispatch` + /// if \p StreamOrStreambuf is a streambuf (i.e. derived from std::streambuf). + /// template class boost_optimized_oarchive : private StreamOrStreambuf, @@ -84,6 +96,8 @@ namespace ttg::detail { using pbase_type = StreamOrStreambuf; using base_type = boost::archive::binary_oarchive_impl, std::ostream::char_type, std::ostream::traits_type>; + // if pbase_type is derived from std::streambuf can use this information to avoid virtual function calls and inline + static constexpr bool pbase_derived_from_stdstreambuf = std::is_base_of_v; private: friend class boost::archive::save_access; @@ -108,9 +122,12 @@ namespace ttg::detail { : pbase_type(std::forward(arg)) , base_type(this->pbase(), boost::archive::no_header | boost::archive::no_codecvt){}; + /// these provide optimized implementation that's called by base_type::save_override + /// @{ + template void save_override(const T& t) { - oarchive_save_override_optimized_dispatch(this->base(), t); + oarchive_save_override_optimized_dispatch(*this, t); } void save_override(const boost::archive::class_id_optional_type& /* t */) {} @@ -121,11 +138,53 @@ namespace ttg::detail { void save_override(const boost::archive::class_id_type& t) {} void save_override(const boost::archive::class_id_reference_type& t) {} + /// @} + void save_object(const void* x, const boost::archive::detail::basic_oserializer& bos) { abort(); } + /// override default implementations in base_type provided by basic_binary_oprimitive + /// @{ + + // default saving of primitives. + template + void save(const T & t) + { + save_binary(& t, sizeof(T)); + } + + // trap usage of invalid uninitialized boolean which would + // otherwise crash on load. + void save(const bool t){ + BOOST_ASSERT(0 == static_cast(t) || 1 == static_cast(t)); + save_binary(& t, sizeof(t)); + } + public: - BOOST_ARCHIVE_DECL - void save_binary(const void* address, std::size_t count); + + // the optimized save_array dispatches to save_binary + template + void save_array(boost::serialization::array_wrapper const& a, unsigned int) + { + save_binary(a.address(),a.count()*sizeof(ValueType)); + } + + void save_binary(const void *address, std::size_t count) { + if constexpr (pbase_derived_from_stdstreambuf) { // if we were given a streambuf use it directly ... + using Elem = std::ostream::char_type; + count = (count + sizeof(Elem) - 1) / sizeof(Elem); + std::streamsize scount = static_cast(this->pbase()) + .sputn(static_cast(address), static_cast(count)); + if (count != static_cast(scount)) + boost::serialization::throw_exception( + boost::archive::archive_exception(boost::archive::archive_exception::output_stream_error)); + } + else { // ... else let boost::archive::basic_binary_oprimitive handle via std::stringbuf + // (and associated virtual function calls ... no inlining for you) + this->base().save_binary(address, count); + } + } + + /// @} template auto& operator<<(const T& t) { @@ -139,7 +198,14 @@ namespace ttg::detail { return *this << t; } - const auto& streambuf() const { return this->pbase(); } + const auto& streambuf() const { + if constexpr (pbase_derived_from_stdstreambuf) { + return static_cast(this->pbase()); + } + else { + return this->pbase(); + } + } const auto& stream() const { return this->pbase(); } }; From 01c961c112eba8de093884419e6417b7c1a6e8eb Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Wed, 28 Feb 2024 11:22:43 -0500 Subject: [PATCH 03/12] [serialization] introduced `boost_byte_{i,o}archive` that use `byte_{i,o}streambuf` --- ttg/ttg/serialization/backends/boost/archive.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ttg/ttg/serialization/backends/boost/archive.h b/ttg/ttg/serialization/backends/boost/archive.h index ce7626e34..37a55f3f3 100644 --- a/ttg/ttg/serialization/backends/boost/archive.h +++ b/ttg/ttg/serialization/backends/boost/archive.h @@ -219,6 +219,9 @@ namespace ttg::detail { using boost_buffer_oarchive = boost_optimized_oarchive>>; + /// an archive that constructs serialized representation of an object in a memory buffer, in an optimized manner + using boost_byte_oarchive = boost_optimized_oarchive; + /// constructs a boost_buffer_oarchive object /// @param[in] buf pointer to a memory buffer to which serialized representation will be written @@ -316,6 +319,9 @@ namespace ttg::detail { using boost_buffer_iarchive = boost_optimized_iarchive>>; + /// the deserializer for boost_byte_oarchive + using boost_byte_iarchive = boost_optimized_iarchive; + /// constructs a boost_buffer_iarchive object /// @param[in] buf pointer to a memory buffer from which serialized representation will be read @@ -359,6 +365,10 @@ BOOST_SERIALIZATION_REGISTER_ARCHIVE(ttg::detail::boost_iovec_iarchive); BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION_FOR_THIS_AND_BASE(ttg::detail::boost_iovec_iarchive); BOOST_SERIALIZATION_REGISTER_ARCHIVE(ttg::detail::boost_buffer_iarchive); BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION_FOR_THIS_AND_BASE(ttg::detail::boost_buffer_iarchive); +BOOST_SERIALIZATION_REGISTER_ARCHIVE(ttg::detail::boost_byte_oarchive); +BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION_FOR_THIS_AND_BASE(ttg::detail::boost_byte_oarchive); +BOOST_SERIALIZATION_REGISTER_ARCHIVE(ttg::detail::boost_byte_iarchive); +BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION_FOR_THIS_AND_BASE(ttg::detail::boost_byte_iarchive); #undef BOOST_SERIALIZATION_USE_ARRAY_OPTIMIZATION_FOR_THIS_AND_BASE From a8a00396b4e3ed540ad4b4b7f585642057257fe6 Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Wed, 28 Feb 2024 11:23:27 -0500 Subject: [PATCH 04/12] [serialization] Boost-based TTG serialization uses boost_byte_oarchive for optimized operation ... hopefully can inline most of the calls --- ttg/ttg/serialization/backends/boost/archive.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ttg/ttg/serialization/backends/boost/archive.h b/ttg/ttg/serialization/backends/boost/archive.h index 37a55f3f3..37fbbb8f0 100644 --- a/ttg/ttg/serialization/backends/boost/archive.h +++ b/ttg/ttg/serialization/backends/boost/archive.h @@ -230,8 +230,7 @@ namespace ttg::detail { /// @return a boost_buffer_oarchive object referring to @p buf inline auto make_boost_buffer_oarchive(void* const buf, std::size_t size, std::size_t buf_offset = 0) { assert(buf_offset <= size); - using arrsink_t = boost::iostreams::basic_array_sink; - return boost_buffer_oarchive(arrsink_t(static_cast(buf) + buf_offset, size - buf_offset)); + return ttg::detail::boost_byte_oarchive(ttg::detail::byte_ostreambuf(static_cast(buf) + buf_offset, size - buf_offset)); } /// constructs a boost_buffer_oarchive object From 3a75af34fb7a158b5b72aecd6195d83f28bec900 Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Wed, 28 Feb 2024 13:33:42 -0500 Subject: [PATCH 05/12] [serialization] added `s{put,get}n` functions to `byte_{i,o}streambuf` to avoid the use of `basic_streambuf::s{get,put}n` --- ttg/ttg/serialization/stream.h | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/ttg/ttg/serialization/stream.h b/ttg/ttg/serialization/stream.h index 9c50e0f83..09304be74 100644 --- a/ttg/ttg/serialization/stream.h +++ b/ttg/ttg/serialization/stream.h @@ -72,9 +72,14 @@ namespace ttg::detail { public: using std::streambuf::streambuf; - byte_ostreambuf(char_type* buffer, std::streamsize buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} + inline byte_ostreambuf(char_type* buffer, std::streamsize buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} - std::streamsize xsputn(const char_type* s, std::streamsize n) override { + // hides basic_streambuf::sputn so can avoid the virtual function dispatch if the compiler is not aggressive enough + inline std::streamsize sputn(const char_type* s, std::streamsize n) noexcept { + return this->xsputn(s, n); + } + + inline std::streamsize xsputn(const char_type* s, std::streamsize n) noexcept override { assert((cursor_ - buffer_) + n <= buffer_size_); std::memcpy(cursor_, s, n * sizeof(char_type)); cursor_ += n; @@ -92,9 +97,14 @@ namespace ttg::detail { public: using std::streambuf::streambuf; - byte_istreambuf(char_type* buffer, std::size_t buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} + inline byte_istreambuf(char_type* buffer, std::size_t buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} - std::streamsize xsgetn(char_type* s, std::streamsize max_n) override { + // hides basic_streambuf::sgetn so can avoid the virtual function dispatch if the compiler is not aggressive enough + inline std::streamsize sgetn(char_type* s, std::streamsize n) noexcept { + return this->xsgetn(s, n); + } + + inline std::streamsize xsgetn(char_type* s, std::streamsize max_n) noexcept override { const auto n_to_read = std::min(buffer_size_ - (cursor_ - buffer_), max_n); std::memcpy(s, cursor_, n_to_read * sizeof(char_type)); cursor_ += n_to_read; From 4c61860172d82438e0d89adcdccac65a78af088c Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Wed, 28 Feb 2024 13:34:18 -0500 Subject: [PATCH 06/12] [serialization] throw -> assert to maybe improve codegen --- ttg/ttg/serialization/backends/boost/archive.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ttg/ttg/serialization/backends/boost/archive.h b/ttg/ttg/serialization/backends/boost/archive.h index 37fbbb8f0..b6d0bb434 100644 --- a/ttg/ttg/serialization/backends/boost/archive.h +++ b/ttg/ttg/serialization/backends/boost/archive.h @@ -174,9 +174,7 @@ namespace ttg::detail { count = (count + sizeof(Elem) - 1) / sizeof(Elem); std::streamsize scount = static_cast(this->pbase()) .sputn(static_cast(address), static_cast(count)); - if (count != static_cast(scount)) - boost::serialization::throw_exception( - boost::archive::archive_exception(boost::archive::archive_exception::output_stream_error)); + assert(count == static_cast(scount)); } else { // ... else let boost::archive::basic_binary_oprimitive handle via std::stringbuf // (and associated virtual function calls ... no inlining for you) From d216befcfa9d36d4b391dfb0e8ca198f30563f79 Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Wed, 28 Feb 2024 16:27:53 -0500 Subject: [PATCH 07/12] [serialization] `byte_{i,o}streambuf::xs{get,put}n` must be `final` to inline --- ttg/ttg/serialization/stream.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ttg/ttg/serialization/stream.h b/ttg/ttg/serialization/stream.h index 09304be74..fe2301f8c 100644 --- a/ttg/ttg/serialization/stream.h +++ b/ttg/ttg/serialization/stream.h @@ -72,14 +72,14 @@ namespace ttg::detail { public: using std::streambuf::streambuf; - inline byte_ostreambuf(char_type* buffer, std::streamsize buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} + byte_ostreambuf(char_type* buffer, std::streamsize buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} // hides basic_streambuf::sputn so can avoid the virtual function dispatch if the compiler is not aggressive enough - inline std::streamsize sputn(const char_type* s, std::streamsize n) noexcept { + std::streamsize sputn(const char_type* s, std::streamsize n) noexcept { return this->xsputn(s, n); } - inline std::streamsize xsputn(const char_type* s, std::streamsize n) noexcept override { + std::streamsize xsputn(const char_type* s, std::streamsize n) noexcept override final { assert((cursor_ - buffer_) + n <= buffer_size_); std::memcpy(cursor_, s, n * sizeof(char_type)); cursor_ += n; @@ -97,14 +97,14 @@ namespace ttg::detail { public: using std::streambuf::streambuf; - inline byte_istreambuf(char_type* buffer, std::size_t buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} + byte_istreambuf(char_type* buffer, std::size_t buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} // hides basic_streambuf::sgetn so can avoid the virtual function dispatch if the compiler is not aggressive enough - inline std::streamsize sgetn(char_type* s, std::streamsize n) noexcept { + std::streamsize sgetn(char_type* s, std::streamsize n) noexcept { return this->xsgetn(s, n); } - inline std::streamsize xsgetn(char_type* s, std::streamsize max_n) noexcept override { + std::streamsize xsgetn(char_type* s, std::streamsize max_n) noexcept override final { const auto n_to_read = std::min(buffer_size_ - (cursor_ - buffer_), max_n); std::memcpy(s, cursor_, n_to_read * sizeof(char_type)); cursor_ += n_to_read; From 957886cd200749a73b0344d4db3b4bfa30d877a1 Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Thu, 29 Feb 2024 11:32:28 -0500 Subject: [PATCH 08/12] [serialization] `boost_byte_iarchive` is also inlinable --- .../serialization/backends/boost/archive.h | 76 +++++++++++++++++-- ttg/ttg/serialization/stream.h | 6 +- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/ttg/ttg/serialization/backends/boost/archive.h b/ttg/ttg/serialization/backends/boost/archive.h index b6d0bb434..e688dbc66 100644 --- a/ttg/ttg/serialization/backends/boost/archive.h +++ b/ttg/ttg/serialization/backends/boost/archive.h @@ -171,6 +171,7 @@ namespace ttg::detail { void save_binary(const void *address, std::size_t count) { if constexpr (pbase_derived_from_stdstreambuf) { // if we were given a streambuf use it directly ... using Elem = std::ostream::char_type; + static_assert(sizeof(Elem) == 1); count = (count + sizeof(Elem) - 1) / sizeof(Elem); std::streamsize scount = static_cast(this->pbase()) .sputn(static_cast(address), static_cast(count)); @@ -240,8 +241,7 @@ namespace ttg::detail { template inline auto make_boost_buffer_oarchive(char (&buf)[N], std::size_t buf_offset = 0) { assert(buf_offset <= N); - using arrsink_t = boost::iostreams::basic_array_sink; - return boost_buffer_oarchive(arrsink_t(&(buf[buf_offset]), N - buf_offset)); + return ttg::detail::boost_byte_oarchive(ttg::detail::byte_ostreambuf(&(buf[buf_offset], N - buf_offset))); } /// optimized data-only deserializer for boost_optimized_oarchive @@ -254,6 +254,8 @@ namespace ttg::detail { using pbase_type = StreamOrStreambuf; using base_type = boost::archive::binary_iarchive_impl; + // if pbase_type is derived from std::streambuf can use this information to avoid virtual function calls and inline + static constexpr bool pbase_derived_from_stdstreambuf = std::is_base_of_v; private: friend class boost::archive::save_access; @@ -278,9 +280,12 @@ namespace ttg::detail { : pbase_type(std::forward(arg)) , base_type(this->pbase(), boost::archive::no_header | boost::archive::no_codecvt){}; + /// these provide optimized implementation that's called by base_type::load_override + /// @{ + template void load_override(T& t) { - iarchive_load_override_optimized_dispatch(this->base(), t); + iarchive_load_override_optimized_dispatch(*this, t); } void load_override(boost::archive::class_id_optional_type& /* t */) {} @@ -291,8 +296,58 @@ namespace ttg::detail { void load_override(boost::archive::class_id_type& t) {} void load_override(boost::archive::class_id_reference_type& t) {} + /// @} + void load_object(void* x, const boost::archive::detail::basic_oserializer& bos) { abort(); } + /// override default implementations in base_type provided by basic_binary_iprimitive + /// @{ + + // main template for serialization of primitive types + template + void load(T & t){ + load_binary(& t, sizeof(T)); + } + + ///////////////////////////////////////////////////////// + // fundamental types that need special treatment + + // trap usage of invalid uninitialized boolean + void load(bool & t){ + load_binary(& t, sizeof(t)); + int i = t; + BOOST_ASSERT(0 == i || 1 == i); + (void)i; // warning suppression for release builds. + } + + public: + + // the optimized load_array dispatches to load_binary + template + void load_array(boost::serialization::array_wrapper& a, unsigned int) + { + load_binary(a.address(),a.count()*sizeof(ValueType)); + } + + void load_binary( + void *address, + std::size_t count + ) { + if constexpr (pbase_derived_from_stdstreambuf) { // if we were given a streambuf use it directly ... + using Elem = std::ostream::char_type; + static_assert(sizeof(Elem) == 1); + std::streamsize s = static_cast(count); + std::streamsize scount = static_cast(this->pbase()).sgetn(static_cast(address), s); + assert(scount == count); + } + else { // ... else let boost::archive::basic_binary_iprimitive handle via std::stringbuf + // (and associated virtual function calls ... no inlining for you) + this->base().load_binary(address, count); + } + } + + /// @} + template auto& operator>>(T& t) { this->load_override(t); @@ -305,7 +360,14 @@ namespace ttg::detail { return *this >> t; } - const auto& streambuf() const { return this->pbase(); } + const auto& streambuf() const { + if constexpr (pbase_derived_from_stdstreambuf) { + return static_cast(this->pbase()); + } + else { + return this->pbase(); + } + } const auto& stream() const { return this->pbase(); } }; @@ -327,8 +389,7 @@ namespace ttg::detail { /// @return a boost_buffer_iarchive object referring to @p buf inline auto make_boost_buffer_iarchive(const void* const buf, std::size_t size, std::size_t buf_offset = 0) { assert(buf_offset <= size); - using arrsrc_t = boost::iostreams::basic_array_source; - return boost_buffer_iarchive(arrsrc_t(static_cast(buf) + buf_offset, size - buf_offset)); + return ttg::detail::boost_byte_iarchive(ttg::detail::byte_istreambuf(static_cast(buf) + buf_offset, size - buf_offset)); } /// constructs a boost_buffer_iarchive object @@ -340,8 +401,7 @@ namespace ttg::detail { template inline auto make_boost_buffer_iarchive(const char (&buf)[N], std::size_t buf_offset = 0) { assert(buf_offset <= N); - using arrsrc_t = boost::iostreams::basic_array_source; - return boost_buffer_iarchive(arrsrc_t(&(buf[buf_offset]), N - buf_offset)); + return ttg::detail::boost_byte_iarchive(ttg::detail::byte_istreambuf((&(buf[buf_offset]), N - buf_offset))); } } // namespace ttg::detail diff --git a/ttg/ttg/serialization/stream.h b/ttg/ttg/serialization/stream.h index fe2301f8c..f5991b330 100644 --- a/ttg/ttg/serialization/stream.h +++ b/ttg/ttg/serialization/stream.h @@ -97,7 +97,7 @@ namespace ttg::detail { public: using std::streambuf::streambuf; - byte_istreambuf(char_type* buffer, std::size_t buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} + byte_istreambuf(const char_type* buffer, std::size_t buffer_size = std::numeric_limits::max()) : buffer_(buffer), cursor_(buffer_), buffer_size_(buffer_size) {} // hides basic_streambuf::sgetn so can avoid the virtual function dispatch if the compiler is not aggressive enough std::streamsize sgetn(char_type* s, std::streamsize n) noexcept { @@ -112,8 +112,8 @@ namespace ttg::detail { } private: - char_type* buffer_; - char_type* cursor_; // current location in buffer_ + const char_type* buffer_; + const char_type* cursor_; // current location in buffer_ std::streamsize buffer_size_; }; From 76c053e31daf936689dc6c011bf9ed5f9b44af5f Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Thu, 29 Feb 2024 11:33:35 -0500 Subject: [PATCH 09/12] [serialization] introduced `byte_{i,o}streambuf::size()` --- ttg/ttg/serialization/stream.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ttg/ttg/serialization/stream.h b/ttg/ttg/serialization/stream.h index f5991b330..848252630 100644 --- a/ttg/ttg/serialization/stream.h +++ b/ttg/ttg/serialization/stream.h @@ -86,6 +86,11 @@ namespace ttg::detail { return n; } + /// number of characters written to the buffer + std::streamsize size() const noexcept { + return cursor_ - buffer_; + } + private: char_type* buffer_; char_type* cursor_; // current location in buffer_ @@ -111,6 +116,11 @@ namespace ttg::detail { return n_to_read; } + /// number of characters read from the buffer + std::streamsize size() const noexcept { + return cursor_ - buffer_; + } + private: const char_type* buffer_; const char_type* cursor_; // current location in buffer_ From 89ced1ad623e328b4c8d4644f45ffa6a54d1f03c Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Thu, 29 Feb 2024 11:45:51 -0500 Subject: [PATCH 10/12] [serialization] default_data_descriptor::pack_payload returns the actual number of bytes written + dox --- ttg/ttg/serialization/data_descriptor.h | 195 ++++++++++++++---------- 1 file changed, 116 insertions(+), 79 deletions(-) diff --git a/ttg/ttg/serialization/data_descriptor.h b/ttg/ttg/serialization/data_descriptor.h index f14d00e72..709810b76 100644 --- a/ttg/ttg/serialization/data_descriptor.h +++ b/ttg/ttg/serialization/data_descriptor.h @@ -15,22 +15,41 @@ #include "ttg/serialization/splitmd_data_descriptor.h" -// This provides an efficent API for serializing/deserializing a data type. -// An object of this type will need to be provided for each serializable type. -// The default implementation, in serialization.h, works only for primitive/POD data types; -// backend-specific implementations may be available in backend/serialization.h . +/// This provides an efficient C API for serializing/deserializing a data type to a nonportable contiguous bytestring. +/// An object of this type will need to be provided for each serializable type. +/// The default implementation, in serialization.h, works only for primitive/POD data types; +/// backend-specific implementations may be available in backend/serialization.h . extern "C" struct ttg_data_descriptor { const char *name; + + /// @brief measures the size of the binary representation of @p object + /// @param[in] object pointer to the object to be serialized + /// @return the number of bytes needed for binary representation of @p object uint64_t (*payload_size)(const void *object); - uint64_t (*pack_payload)(const void *object, uint64_t chunk_size, uint64_t pos, void *buf); - void (*unpack_payload)(void *object, uint64_t chunk_size, uint64_t pos, const void *buf); + + /// @brief serializes object to a buffer + /// @param[in] object pointer to the object to be serialized + /// @param[in] max_nbytes_to_write the maximum number of bytes to write + /// @param[in] offset the position in \p buf where the first byte of serialized data will be written + /// @param[in,out] buf the data buffer that will contain the serialized representation of the object + /// @return position in \p buf after the last byte written + uint64_t (*pack_payload)(const void *object, uint64_t max_nbytes_to_write, uint64_t offset, void *buf); + + /// @brief deserializes object from a buffer + /// @param[in] object pointer to the object to be deserialized + /// @param[in] max_nbytes_to_read the maximum number of bytes to read + /// @param[in] offset the position in \p buf where the first byte of serialized data will be read + /// @param[in] buf the data buffer that contains the serialized representation of the object + /// @return position in \p buf after the last byte written + void (*unpack_payload)(void *object, uint64_t max_nbytes_to_read, uint64_t offset, const void *buf); + void (*print)(const void *object); }; namespace ttg { - /** - * \brief Provides (de)serialization of C++ data invocable from C primarily to interface with PaRSEC +/** + * \brief Provides (de)serialization of C++ data that can be invoked from C via ttg_data_descriptor * The default implementation is only provided for POD data types but is efficient in the sense that * it does enable zero-copy remote data transfers. For other data types, optimized implementations @@ -40,7 +59,7 @@ namespace ttg { template struct default_data_descriptor; - /// default_data_descriptor for trivially-copyable types + /// @brief default_data_descriptor for trivially-copyable types /// @tparam T a trivially-copyable type template struct default_data_descriptor< @@ -48,43 +67,46 @@ namespace ttg { !ttg::has_split_metadata::value>> { static constexpr const bool serialize_size_is_const = true; + /// @brief measures the size of the binary representation of @p object /// @param[in] object pointer to the object to be serialized - /// @return size of serialized @p object + /// @return the number of bytes needed for binary representation of @p object static uint64_t payload_size(const void *object) { return static_cast(sizeof(T)); } /// @brief serializes object to a buffer - /// @param[in] object pointer to the object to be serialized - /// @param[in] size the size of @p object in bytes - /// @param[in] begin location in @p buf where the first byte of serialized data will be written - /// @param[in,out] buf the data buffer that will contain serialized data - /// @return location in @p buf after the last byte written - static uint64_t pack_payload(const void *object, uint64_t size, uint64_t begin, void *buf) { + /// @param[in] max_nbytes_to_write the maximum number of bytes to write + /// @param[in] offset the position in \p buf where the first byte of serialized data will be written + /// @param[in,out] buf the data buffer that will contain the serialized representation of the object + /// @return position in \p buf after the last byte written + static uint64_t pack_payload(const void *object, uint64_t max_nbytes_to_write, uint64_t begin, void *buf) { unsigned char *char_buf = reinterpret_cast(buf); - std::memcpy(&char_buf[begin], object, size); - return begin + size; + assert(sizeof(T)<=max_nbytes_to_write); + std::memcpy(&char_buf[begin], object, sizeof(T)); + return begin + sizeof(T); } /// @brief deserializes object from a buffer - - /// @param[in,out] object pointer to the object to be deserialized - /// @param[in] size the size of @p object in bytes - /// @param[in] begin location in @p buf where the first byte of serialized data will be read - /// @param[in] buf the data buffer that contains serialized data - static void unpack_payload(void *object, uint64_t size, uint64_t begin, const void *buf) { + /// @param[in] object pointer to the object to be deserialized + /// @param[in] max_nbytes_to_read the maximum number of bytes to read + /// @param[in] offset the position in \p buf where the first byte of serialized data will be read + /// @param[in] buf the data buffer that contains the serialized representation of the object + /// @return position in \p buf after the last byte written + static void unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t begin, const void *buf) { const unsigned char *char_buf = reinterpret_cast(buf); - std::memcpy(object, &char_buf[begin], size); + assert(sizeof(T)<=max_nbytes_to_read); + std::memcpy(object, &char_buf[begin], sizeof(T)); } }; - /// default_data_descriptor for trivially-copyable types - /// @tparam T a trivially-copyable type + /// @brief default_data_descriptor for types that support 2-stage serialization (metadata first, then the rest) for implementing zero-copy transfers + /// @tparam T a type for which `ttg::has_split_metadata::value` is true template struct default_data_descriptor::value>> { static constexpr const bool serialize_size_is_const = false; + /// @brief measures the size of the binary representation of @p object /// @param[in] object pointer to the object to be serialized - /// @return size of serialized @p object + /// @return the number of bytes needed for binary representation of @p object static uint64_t payload_size(const void *object) { SplitMetadataDescriptor smd; const T *t = reinterpret_cast(object); @@ -98,39 +120,40 @@ namespace ttg { } /// @brief serializes object to a buffer - /// @param[in] object pointer to the object to be serialized - /// @param[in] size the size of @p object in bytes - /// @param[in] begin location in @p buf where the first byte of serialized data will be written - /// @param[in,out] buf the data buffer that will contain serialized data - /// @return location in @p buf after the last byte written - static uint64_t pack_payload(const void *object, uint64_t size, uint64_t begin, void *buf) { + /// @param[in] max_nbytes_to_write the maximum number of bytes to write + /// @param[in] offset the position in \p buf where the first byte of serialized data will be written + /// @param[in,out] buf the data buffer that will contain the serialized representation of the object + /// @return position in \p buf after the last byte written + static uint64_t pack_payload(const void *object, uint64_t max_nbytes_to_write, uint64_t begin, void *buf) { SplitMetadataDescriptor smd; const T *t = reinterpret_cast(object); unsigned char *char_buf = reinterpret_cast(buf); auto metadata = smd.get_metadata(t); + assert(sizeof(metadata) <= max_nbytes_to_write); std::memcpy(&char_buf[begin], metadata, sizeof(metadata)); size_t pos = sizeof(metadata); for (auto &&iovec : smd.get_data(t)) { std::memcpy(&char_buf[begin + pos], iovec.data, iovec.num_bytes); pos += iovec.num_bytes; - assert(pos < size); + assert(pos <= max_nbytes_to_write); } - return begin + size; + return begin + pos; } /// @brief deserializes object from a buffer - - /// @param[in,out] object pointer to the object to be deserialized - /// @param[in] size the size of @p object in bytes - /// @param[in] begin location in @p buf where the first byte of serialized data will be read - /// @param[in] buf the data buffer that contains serialized data - static void unpack_payload(void *object, uint64_t size, uint64_t begin, const void *buf) { + /// @param[in] object pointer to the object to be deserialized + /// @param[in] max_nbytes_to_read the maximum number of bytes to read + /// @param[in] offset the position in \p buf where the first byte of serialized data will be read + /// @param[in] buf the data buffer that contains the serialized representation of the object + /// @return position in \p buf after the last byte written + static void unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t begin, const void *buf) { SplitMetadataDescriptor smd; T *t = reinterpret_cast(object); using metadata_t = decltype(smd.get_metadata(t)); + assert(sizeof(metadata_t) <= max_nbytes_to_read); const unsigned char *char_buf = reinterpret_cast(buf); const metadata_t *metadata = reinterpret_cast(char_buf + begin); T t_created = smd.create_from_metadata(); @@ -139,7 +162,7 @@ namespace ttg { for (auto &&iovec : smd.get_data(t)) { std::memcpy(iovec.data, &char_buf[begin + pos], iovec.num_bytes); pos += iovec.num_bytes; - assert(pos < size); + assert(pos <= max_nbytes_to_read); } } }; @@ -150,39 +173,45 @@ namespace ttg { namespace ttg { - /// The default implementation for non-POD data types that are not directly copyable - /// and support MADNESS serialization + /// @brief default_data_descriptor for non-POD data types that are not directly copyable or 2-stage serializable and support MADNESS serialization template struct default_data_descriptor< T, std::enable_if_t<((!detail::is_memcpyable_v && detail::is_madness_buffer_serializable_v) || detail::is_madness_user_buffer_serializable_v)&&!ttg::has_split_metadata::value>> { static constexpr const bool serialize_size_is_const = false; + /// @brief measures the size of the binary representation of @p object + /// @param[in] object pointer to the object to be serialized + /// @return the number of bytes needed for binary representation of @p object static uint64_t payload_size(const void *object) { madness::archive::BufferOutputArchive ar; - ar &(*(T *)object); + ar & (*static_cast>>(object)); return static_cast(ar.size()); } - /// object --- obj to be serialized - /// chunk_size --- inputs max amount of data to output, and on output returns amount actually output - /// pos --- position in the input buffer to resume serialization - /// buf[pos] --- place for output - static uint64_t pack_payload(const void *object, uint64_t chunk_size, uint64_t pos, void *_buf) { + /// @brief serializes object to a buffer + /// @param[in] object pointer to the object to be serialized + /// @param[in] max_nbytes_to_write the maximum number of bytes to write + /// @param[in] offset the position in \p buf where the first byte of serialized data will be written + /// @param[in,out] buf the data buffer that will contain the serialized representation of the object + /// @return position in \p buf after the last byte written + static uint64_t pack_payload(const void *object, uint64_t max_nbytes_to_write, uint64_t pos, void *_buf) { unsigned char *buf = reinterpret_cast(_buf); - madness::archive::BufferOutputArchive ar(&buf[pos], chunk_size); - ar &(*(T *)object); - return pos + chunk_size; + madness::archive::BufferOutputArchive ar(&buf[pos], max_nbytes_to_write); + ar & (*static_cast>>(object)); + return pos + ar.size(); } - /// object --- obj to be deserialized - /// chunk_size --- amount of data for input - /// pos --- position in the input buffer to resume deserialization - /// object -- pointer to the object to fill up - static void unpack_payload(void *object, uint64_t chunk_size, uint64_t pos, const void *_buf) { + /// @brief deserializes object from a buffer + /// @param[in] object pointer to the object to be deserialized + /// @param[in] max_nbytes_to_read the maximum number of bytes to read + /// @param[in] offset the position in \p buf where the first byte of serialized data will be read + /// @param[in] buf the data buffer that contains the serialized representation of the object + /// @return position in \p buf after the last byte written + static void unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t pos, const void *_buf) { const unsigned char *buf = reinterpret_cast(_buf); - madness::archive::BufferInputArchive ar(&buf[pos], chunk_size); - ar &(*(T *)object); + madness::archive::BufferInputArchive ar(&buf[pos], max_nbytes_to_read); + ar & (*static_cast>(object)); } }; @@ -196,8 +225,7 @@ namespace ttg { namespace ttg { - /// The default implementation for non-POD data types that are not directly copyable, - /// do not support MADNESS serialization, and support Boost serialization + /// @brief default_data_descriptor for non-POD data types that are not directly copyable, not 2-stage serializable, do not support MADNESS serialization, and support Boost serialization template struct default_data_descriptor< T, std::enable_if_t<(!detail::is_memcpyable_v && !detail::is_madness_buffer_serializable_v && @@ -206,29 +234,38 @@ namespace ttg { detail::is_boost_user_buffer_serializable_v)>> { static constexpr const bool serialize_size_is_const = false; + /// @brief measures the size of the binary representation of @p object + /// @param[in] object pointer to the object to be serialized + /// @return the number of bytes needed for binary representation of @p object static uint64_t payload_size(const void *object) { ttg::detail::boost_counting_oarchive oa; - oa << (*(T *)object); + oa << (*static_cast>>(object)); return oa.streambuf().size(); } - /// object --- obj to be serialized - /// chunk_size --- inputs max amount of data to output, and on output returns amount actually output - /// pos --- position in the input buffer to resume serialization - /// buf[pos] --- place for output - static uint64_t pack_payload(const void *object, uint64_t chunk_size, uint64_t pos, void *_buf) { - auto oa = ttg::detail::make_boost_buffer_oarchive(_buf, pos + chunk_size, pos); - oa << (*(T *)object); - return pos + chunk_size; + /// @brief serializes object to a buffer + /// @param[in] object pointer to the object to be serialized + /// @param[in] max_nbytes_to_write the maximum number of bytes to write + /// @param[in] offset the position in \p buf where the first byte of serialized data will be written + /// @param[in,out] buf the data buffer that will contain the serialized representation of the object + /// @return position in \p buf after the last byte written + static uint64_t pack_payload(const void *object, uint64_t max_nbytes_to_write, uint64_t pos, void *buf) { + auto oa = ttg::detail::make_boost_buffer_oarchive(buf, pos + max_nbytes_to_write, pos); + oa << (*static_cast>>(object)); + assert(oa.streambuf().size() <= max_nbytes_to_write); + return pos + oa.streambuf().size(); } - /// object --- obj to be deserialized - /// chunk_size --- amount of data for input - /// pos --- position in the input buffer to resume deserialization - /// object -- pointer to the object to fill up - static void unpack_payload(void *object, uint64_t chunk_size, uint64_t pos, const void *_buf) { - auto ia = ttg::detail::make_boost_buffer_iarchive(_buf, pos + chunk_size, pos); - ia >> (*(T *)object); + /// @brief deserializes object from a buffer + /// @param[in] object pointer to the object to be deserialized + /// @param[in] max_nbytes_to_read the maximum number of bytes to read + /// @param[in] offset the position in \p buf where the first byte of serialized data will be read + /// @param[in] buf the data buffer that contains the serialized representation of the object + /// @return position in \p buf after the last byte written + static void unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t pos, const void *buf) { + auto ia = ttg::detail::make_boost_buffer_iarchive(buf, pos + max_nbytes_to_read, pos); + ia >> (*static_cast>(object)); + assert(ia.streambuf().size() <= max_nbytes_to_read); } }; From 84ea6f41382244137bc8b8baf41a99bd9be1bb93 Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Thu, 29 Feb 2024 11:54:19 -0500 Subject: [PATCH 11/12] [serialization] default_data_descriptor::unpack_payload returns the actual number of bytes read to be symmetric wrt pack_payload --- tests/unit/serialization.cc | 4 ++-- ttg/ttg/parsec/ttg.h | 14 ++++++-------- ttg/ttg/serialization/data_descriptor.h | 24 ++++++++++++++---------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/tests/unit/serialization.cc b/tests/unit/serialization.cc index e12607de3..772feaea6 100644 --- a/tests/unit/serialization.cc +++ b/tests/unit/serialization.cc @@ -504,7 +504,7 @@ TEST_CASE("MADNESS Serialization", "[serialization]") { T g_obj; void* g = (void*)&g_obj; - CHECK_NOTHROW(d->unpack_payload(g, obj_size, 0, buf.get())); + CHECK(d->unpack_payload(g, obj_size, 0, buf.get()) == pos); }; test(99); @@ -755,7 +755,7 @@ TEST_CASE("TTG Serialization", "[serialization]") { T g_obj; void* g = (void*)&g_obj; - CHECK_NOTHROW(d->unpack_payload(g, obj_size, 0, buf.get())); + CHECK(d->unpack_payload(g, obj_size, 0, buf.get()) == pos); }; test(99); diff --git a/ttg/ttg/parsec/ttg.h b/ttg/ttg/parsec/ttg.h index 65e39991a..1c31e69ec 100644 --- a/ttg/ttg/parsec/ttg.h +++ b/ttg/ttg/parsec/ttg.h @@ -1835,13 +1835,12 @@ ttg::abort(); // should not happen uint64_t payload_size; if constexpr (!ttg::default_data_descriptor>::serialize_size_is_const) { const ttg_data_descriptor *dSiz = ttg::get_data_descriptor(); - dSiz->unpack_payload(&payload_size, sizeof(uint64_t), pos, _bytes); - pos += sizeof(uint64_t); + pos = dSiz->unpack_payload(&payload_size, sizeof(uint64_t), pos, _bytes); } else { payload_size = dObj->payload_size(&obj); } - dObj->unpack_payload(&obj, payload_size, pos, _bytes); - return pos + payload_size; + pos = dObj->unpack_payload(&obj, payload_size, pos, _bytes); + return pos; } template @@ -1855,11 +1854,10 @@ ttg::abort(); // should not happen if constexpr (!ttg::default_data_descriptor>::serialize_size_is_const) { const ttg_data_descriptor *dSiz = ttg::get_data_descriptor(); - dSiz->pack_payload(&payload_size, sizeof(uint64_t), pos, bytes); - pos += sizeof(uint64_t); + pos = dSiz->pack_payload(&payload_size, sizeof(uint64_t), pos, bytes); } - dObj->pack_payload(&obj, payload_size, pos, bytes); - return pos + payload_size; + pos = dObj->pack_payload(&obj, payload_size, pos, bytes); + return pos; } static void static_set_arg(void *data, std::size_t size, ttg::TTBase *bop) { diff --git a/ttg/ttg/serialization/data_descriptor.h b/ttg/ttg/serialization/data_descriptor.h index 709810b76..da9bb1088 100644 --- a/ttg/ttg/serialization/data_descriptor.h +++ b/ttg/ttg/serialization/data_descriptor.h @@ -40,8 +40,8 @@ extern "C" struct ttg_data_descriptor { /// @param[in] max_nbytes_to_read the maximum number of bytes to read /// @param[in] offset the position in \p buf where the first byte of serialized data will be read /// @param[in] buf the data buffer that contains the serialized representation of the object - /// @return position in \p buf after the last byte written - void (*unpack_payload)(void *object, uint64_t max_nbytes_to_read, uint64_t offset, const void *buf); + /// @return position in \p buf after the last byte read + uint64_t (*unpack_payload)(void *object, uint64_t max_nbytes_to_read, uint64_t offset, const void *buf); void (*print)(const void *object); }; @@ -90,11 +90,12 @@ namespace ttg { /// @param[in] max_nbytes_to_read the maximum number of bytes to read /// @param[in] offset the position in \p buf where the first byte of serialized data will be read /// @param[in] buf the data buffer that contains the serialized representation of the object - /// @return position in \p buf after the last byte written - static void unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t begin, const void *buf) { + /// @return position in \p buf after the last byte read + static uint64_t unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t begin, const void *buf) { const unsigned char *char_buf = reinterpret_cast(buf); assert(sizeof(T)<=max_nbytes_to_read); std::memcpy(object, &char_buf[begin], sizeof(T)); + return begin + sizeof(T); } }; @@ -147,8 +148,8 @@ namespace ttg { /// @param[in] max_nbytes_to_read the maximum number of bytes to read /// @param[in] offset the position in \p buf where the first byte of serialized data will be read /// @param[in] buf the data buffer that contains the serialized representation of the object - /// @return position in \p buf after the last byte written - static void unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t begin, const void *buf) { + /// @return position in \p buf after the last byte read + static uint64_t unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t begin, const void *buf) { SplitMetadataDescriptor smd; T *t = reinterpret_cast(object); @@ -164,6 +165,7 @@ namespace ttg { pos += iovec.num_bytes; assert(pos <= max_nbytes_to_read); } + return begin + pos; } }; @@ -207,11 +209,12 @@ namespace ttg { /// @param[in] max_nbytes_to_read the maximum number of bytes to read /// @param[in] offset the position in \p buf where the first byte of serialized data will be read /// @param[in] buf the data buffer that contains the serialized representation of the object - /// @return position in \p buf after the last byte written - static void unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t pos, const void *_buf) { + /// @return position in \p buf after the last byte read + static uint64_t unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t pos, const void *_buf) { const unsigned char *buf = reinterpret_cast(_buf); madness::archive::BufferInputArchive ar(&buf[pos], max_nbytes_to_read); ar & (*static_cast>(object)); + return pos + (max_nbytes_to_read - ar.nbyte_avail()); } }; @@ -261,11 +264,12 @@ namespace ttg { /// @param[in] max_nbytes_to_read the maximum number of bytes to read /// @param[in] offset the position in \p buf where the first byte of serialized data will be read /// @param[in] buf the data buffer that contains the serialized representation of the object - /// @return position in \p buf after the last byte written - static void unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t pos, const void *buf) { + /// @return position in \p buf after the last byte read + static uint64_t unpack_payload(void *object, uint64_t max_nbytes_to_read, uint64_t pos, const void *buf) { auto ia = ttg::detail::make_boost_buffer_iarchive(buf, pos + max_nbytes_to_read, pos); ia >> (*static_cast>(object)); assert(ia.streambuf().size() <= max_nbytes_to_read); + return pos + ia.streambuf().size(); } }; From c013f36bd432c3ed2f4f3b0126fe15cbec72d493 Mon Sep 17 00:00:00 2001 From: Eduard Valeyev Date: Thu, 29 Feb 2024 15:21:27 -0500 Subject: [PATCH 12/12] [serialization] remove the uses of get_data_descriptor in PaRSEC backend, use default_data_descriptor instead directly --- ttg/ttg/parsec/ttg.h | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/ttg/ttg/parsec/ttg.h b/ttg/ttg/parsec/ttg.h index 1c31e69ec..f4653f962 100644 --- a/ttg/ttg/parsec/ttg.h +++ b/ttg/ttg/parsec/ttg.h @@ -1831,32 +1831,30 @@ ttg::abort(); // should not happen protected: template uint64_t unpack(T &obj, void *_bytes, uint64_t pos) { - const ttg_data_descriptor *dObj = ttg::get_data_descriptor>(); + using dd_t = ttg::default_data_descriptor>; uint64_t payload_size; - if constexpr (!ttg::default_data_descriptor>::serialize_size_is_const) { - const ttg_data_descriptor *dSiz = ttg::get_data_descriptor(); - pos = dSiz->unpack_payload(&payload_size, sizeof(uint64_t), pos, _bytes); + if constexpr (!dd_t::serialize_size_is_const) { + pos = ttg::default_data_descriptor::unpack_payload(&payload_size, sizeof(uint64_t), pos, _bytes); } else { - payload_size = dObj->payload_size(&obj); + payload_size = dd_t::payload_size(&obj); } - pos = dObj->unpack_payload(&obj, payload_size, pos, _bytes); + pos = dd_t::unpack_payload(&obj, payload_size, pos, _bytes); return pos; } template uint64_t pack(T &obj, void *bytes, uint64_t pos, detail::ttg_data_copy_t *copy = nullptr) { - const ttg_data_descriptor *dObj = ttg::get_data_descriptor>(); - uint64_t payload_size = dObj->payload_size(&obj); + using dd_t = ttg::default_data_descriptor>; + uint64_t payload_size = dd_t::payload_size(&obj); if (copy) { /* reset any tracked data, we don't care about the packing from the payload size */ copy->iovec_reset(); } - if constexpr (!ttg::default_data_descriptor>::serialize_size_is_const) { - const ttg_data_descriptor *dSiz = ttg::get_data_descriptor(); - pos = dSiz->pack_payload(&payload_size, sizeof(uint64_t), pos, bytes); + if constexpr (!dd_t::serialize_size_is_const) { + pos = ttg::default_data_descriptor::pack_payload(&payload_size, sizeof(uint64_t), pos, bytes); } - pos = dObj->pack_payload(&obj, payload_size, pos, bytes); + pos = dd_t::pack_payload(&obj, payload_size, pos, bytes); return pos; }