]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Optimize pack()/unpack() for std::vector<T> with trivially copyable T.
authorWolfgang Bangerth <bangerth@colostate.edu>
Sun, 22 May 2022 21:14:48 +0000 (15:14 -0600)
committerWolfgang Bangerth <bangerth@colostate.edu>
Sun, 22 May 2022 21:14:48 +0000 (15:14 -0600)
examples/step-47/doc/intro.dox
include/deal.II/base/utilities.h
include/deal.II/lac/utilities.h

index e93577182b246cc5b67222a8e5d51fd224ab039d..36992df2457c2e328492e467222609fe52d19cdd 100644 (file)
@@ -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.
 
index 00620c633e44497f550abd4ee520be9b024ad4c1..92b3df3cd801ba53fc949ec4697b4d21affd9268 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <deal.II/base/exceptions.h>
 
+#include <cstddef>
 #include <functional>
 #include <string>
 #include <tuple>
@@ -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<T> where T is a type that
+     * satisfies std::is_trivially_copyable<T>::value == true.
+     */
+    template <typename T>
+    struct IsVectorOfTriviallyCopyable
+    {
+      static constexpr bool value = false;
+    };
+
+
+
+    template <typename T>
+    struct IsVectorOfTriviallyCopyable<std::vector<T>>
+    {
+      static constexpr bool value = std::is_trivially_copyable<T>::value;
+    };
+
+
+
+    /**
+     * A function that is used to append the contents of a
+     * std::vector<T> (where T is a type that
+     * satisfies std::is_trivially_copyable<T>::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 <typename T>
+    inline void
+    append_vector_of_trivially_copyable_to_buffer(const T &,
+                                                  std::vector<char> &)
+    {
+      // We shouldn't get here:
+      Assert(false, ExcInternalError());
+    }
+
+
+    template <typename T,
+              typename = std::enable_if_t<std::is_trivially_copyable<T>::value>>
+    inline void
+    append_vector_of_trivially_copyable_to_buffer(
+      const std::vector<T> &object,
+      std::vector<char> &   dest_buffer)
+    {
+      const auto current_position                          = dest_buffer.size();
+      const typename std::vector<T>::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 <typename T>
+    inline void
+    create_vector_of_trivially_copyable_from_buffer(
+      const std::vector<char>::const_iterator &,
+      const std::vector<char>::const_iterator &,
+      T &)
+    {
+      // We shouldn't get here:
+      Assert(false, ExcInternalError());
+    }
+
+
+
+    template <typename T,
+              typename = std::enable_if_t<std::is_trivially_copyable<T>::value>>
+    inline void
+    create_vector_of_trivially_copyable_from_buffer(
+      const std::vector<char>::const_iterator &cbegin,
+      const std::vector<char>::const_iterator &cend,
+      std::vector<T> &                         object)
+    {
+      // First get the size of the vector, and resize the output object
+      typename std::vector<T>::size_type vector_size;
+      memcpy(&vector_size, &*cbegin, sizeof(vector_size));
+      object.resize(vector_size);
+
+      Assert(static_cast<std::ptrdiff_t>(cend - cbegin) ==
+               static_cast<std::ptrdiff_t>(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 <typename T>
   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<T>::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 <typename T>
   T
   unpack(const std::vector<char>::const_iterator &cbegin,
          const std::vector<char>::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<T>() && 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<T>::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 {};
   }
 
 
index 7f54121c5ee91a0f57186e22cd697b1dbbea6f85..414bcc7e80a919828771dd845908c047e5ec5d26 100644 (file)
@@ -393,14 +393,14 @@ namespace Utilities
       std::vector<double>   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));
 

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.