From d4d6b459c14c4ce0662ec17c583ac31b65abac1e Mon Sep 17 00:00:00 2001 From: Wolfgang Bangerth Date: Sun, 22 May 2022 15:14:48 -0600 Subject: [PATCH] Optimize pack()/unpack() for std::vector with trivially copyable T. --- examples/step-47/doc/intro.dox | 2 +- include/deal.II/base/utilities.h | 161 ++++++++++++++++++++++++++++++- include/deal.II/lac/utilities.h | 16 +-- 3 files changed, 167 insertions(+), 12 deletions(-) diff --git a/examples/step-47/doc/intro.dox b/examples/step-47/doc/intro.dox index e93577182b..36992df245 100644 --- a/examples/step-47/doc/intro.dox +++ b/examples/step-47/doc/intro.dox @@ -226,7 +226,7 @@ alternative method. We base this program on the $C^0$ IP method presented by Susanne Brenner and Li-Yeng Sung in the paper "C$^0$ Interior Penalty Method for Linear Fourth Order Boundary Value Problems on polygonal -domains'' @cite Brenner2005 , where the method is +domains" @cite Brenner2005 , where the method is derived for the biharmonic equation with "clamped" boundary conditions. diff --git a/include/deal.II/base/utilities.h b/include/deal.II/base/utilities.h index 00620c633e..92b3df3cd8 100644 --- a/include/deal.II/base/utilities.h +++ b/include/deal.II/base/utilities.h @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -1214,6 +1215,116 @@ namespace Utilities // --------------------- non-inline functions + namespace internal + { + /** + * A structure that is used to identify whether a template + * argument is a std::vector where T is a type that + * satisfies std::is_trivially_copyable::value == true. + */ + template + struct IsVectorOfTriviallyCopyable + { + static constexpr bool value = false; + }; + + + + template + struct IsVectorOfTriviallyCopyable> + { + static constexpr bool value = std::is_trivially_copyable::value; + }; + + + + /** + * A function that is used to append the contents of a + * std::vector (where T is a type that + * satisfies std::is_trivially_copyable::value == true) + * bit for bit to a character array. + * + * If the type is not such a vector of T, then the function + * throws an exception. + */ + template + inline void + append_vector_of_trivially_copyable_to_buffer(const T &, + std::vector &) + { + // We shouldn't get here: + Assert(false, ExcInternalError()); + } + + + template ::value>> + inline void + append_vector_of_trivially_copyable_to_buffer( + const std::vector &object, + std::vector & dest_buffer) + { + const auto current_position = dest_buffer.size(); + const typename std::vector::size_type vector_size = object.size(); + + // Resize the buffer so that it can store the size of 'object' + // as well as all of its elements. + dest_buffer.resize(dest_buffer.size() + sizeof(vector_size) + + object.size() * sizeof(T)); + + // Then copy first the size and then the elements: + memcpy(&dest_buffer[current_position], &vector_size, sizeof(vector_size)); + if (object.size() > 0) + memcpy(&dest_buffer[current_position] + sizeof(vector_size), + object.data(), + object.size() * sizeof(T)); + } + + + + template + inline void + create_vector_of_trivially_copyable_from_buffer( + const std::vector::const_iterator &, + const std::vector::const_iterator &, + T &) + { + // We shouldn't get here: + Assert(false, ExcInternalError()); + } + + + + template ::value>> + inline void + create_vector_of_trivially_copyable_from_buffer( + const std::vector::const_iterator &cbegin, + const std::vector::const_iterator &cend, + std::vector & object) + { + // First get the size of the vector, and resize the output object + typename std::vector::size_type vector_size; + memcpy(&vector_size, &*cbegin, sizeof(vector_size)); + object.resize(vector_size); + + Assert(static_cast(cend - cbegin) == + static_cast(sizeof(vector_size) + + vector_size * sizeof(T)), + ExcMessage("The given buffer has the wrong size.")); + (void)cend; + + // Then copy the elements: + if (object.size() > 0) + memcpy(object.data(), + &*cbegin + sizeof(vector_size), + vector_size * sizeof(T)); + } + + } // namespace internal + + + template size_t pack(const T & object, @@ -1222,6 +1333,7 @@ namespace Utilities { std::size_t size = 0; + // see if the object is small and copyable via memcpy. if so, use // this fast path. otherwise, we have to go through the BOOST // serialization machinery @@ -1239,6 +1351,25 @@ namespace Utilities size = sizeof(T); } + // Next try if we have a vector of trivially copyable objects. + // If that is the case, we can shortcut the whole BOOST serialization + // machinery and just copy the content of the vector bit for bit + // into the output buffer, assuming that we are not asked to compress + // the data. + else if (internal::IsVectorOfTriviallyCopyable::value && + (allow_compression == false)) + { + const std::size_t previous_size = dest_buffer.size(); + + // When we have DEAL_II_HAVE_CXX17 set by default, we can just + // inline the code of the following function here and make the 'if' + // above a 'if constexpr'. Without the 'constexpr', we need to keep + // the general template of the function that throws an exception. + internal::append_vector_of_trivially_copyable_to_buffer(object, + dest_buffer); + + size = dest_buffer.size() - previous_size; + } else { // use buffer as the target of a compressing @@ -1276,14 +1407,13 @@ namespace Utilities } + template T unpack(const std::vector::const_iterator &cbegin, const std::vector::const_iterator &cend, const bool allow_compression) { - T object; - // see if the object is small and copyable via memcpy. if so, use // this fast path. otherwise, we have to go through the BOOST // serialization machinery @@ -1293,9 +1423,31 @@ namespace Utilities if (std::is_trivially_copyable() && sizeof(T) < 256) #endif { + T object; + (void)allow_compression; Assert(std::distance(cbegin, cend) == sizeof(T), ExcInternalError()); std::memcpy(&object, &*cbegin, sizeof(T)); + + return object; + } + // Next try if we have a vector of trivially copyable objects. + // If that is the case, we can shortcut the whole BOOST serialization + // machinery and just copy the content of the buffer bit for bit + // into an appropriately sized output vector, assuming that we + // are not asked to compress the data. + else if (internal::IsVectorOfTriviallyCopyable::value && + (allow_compression == false)) + { + // When we have DEAL_II_HAVE_CXX17 set by default, we can just + // inline the code of the following function here and make the 'if' + // above a 'if constexpr'. Without the 'constexpr', we need to keep + // the general template of the function that throws an exception. + T object; + internal::create_vector_of_trivially_copyable_from_buffer(cbegin, + cend, + object); + return object; } else { @@ -1310,10 +1462,13 @@ namespace Utilities fisb.push(boost::iostreams::array_source(&*cbegin, cend - cbegin)); boost::archive::binary_iarchive bia(fisb); + + T object; bia >> object; + return object; } - return object; + return {}; } diff --git a/include/deal.II/lac/utilities.h b/include/deal.II/lac/utilities.h index 7f54121c5e..414bcc7e80 100644 --- a/include/deal.II/lac/utilities.h +++ b/include/deal.II/lac/utilities.h @@ -393,14 +393,14 @@ namespace Utilities std::vector work; // ^^ types::blas_int info; // call lapack_templates.h wrapper: - internal::UtilitiesImplementation::call_stev('N', - n, - diagonal.data(), - subdiagonal.data(), - Z.data(), - ldz, - work.data(), - &info); + dealii::internal::UtilitiesImplementation::call_stev('N', + n, + diagonal.data(), + subdiagonal.data(), + Z.data(), + ldz, + work.data(), + &info); Assert(info == 0, LAPACKSupport::ExcErrorCode("dstev", info)); -- 2.39.5