From 3fd302cdb7c787daf9bfc26d7a03470bf1bdcd00 Mon Sep 17 00:00:00 2001 From: Wolfgang Bangerth Date: Thu, 26 May 2022 13:39:05 -0600 Subject: [PATCH] Optimize Utilities::pack/unpack for empty objects. --- include/deal.II/base/utilities.h | 30 ++++++++--- tests/base/utilities_pack_unpack_08.cc | 60 ++++++++++++++++++++++ tests/base/utilities_pack_unpack_08.output | 4 ++ 3 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 tests/base/utilities_pack_unpack_08.cc create mode 100644 tests/base/utilities_pack_unpack_08.output diff --git a/include/deal.II/base/utilities.h b/include/deal.II/base/utilities.h index 3deb58189e..87f676f64b 100644 --- a/include/deal.II/base/utilities.h +++ b/include/deal.II/base/utilities.h @@ -1456,13 +1456,20 @@ namespace Utilities if (std::is_trivially_copyable() && sizeof(T) < 256) #endif { + // Determine the size. There are places where we would like to use a + // truly empty type, for which we use std::tuple<> (i.e., a tuple + // of zero elements). For this class, the compiler reports a nonzero + // sizeof(...) because that is the minimum possible for objects -- + // objects need to have distinct addresses, so they need to have a size + // of at least one. But we can special case this situation. + size = (std::is_same>::value ? 0 : sizeof(T)); + (void)allow_compression; const std::size_t previous_size = dest_buffer.size(); - dest_buffer.resize(previous_size + sizeof(T)); - - std::memcpy(dest_buffer.data() + previous_size, &object, sizeof(T)); + dest_buffer.resize(previous_size + size); - size = sizeof(T); + if (size > 0) + std::memcpy(dest_buffer.data() + previous_size, &object, size); } // Next try if we have a vector of trivially copyable objects. // If that is the case, we can shortcut the whole BOOST serialization @@ -1536,11 +1543,22 @@ namespace Utilities if (std::is_trivially_copyable() && sizeof(T) < 256) #endif { + // Determine the size. There are places where we would like to use a + // truly empty type, for which we use std::tuple<> (i.e., a tuple + // of zero elements). For this class, the compiler reports a nonzero + // sizeof(...) because that is the minimum possible for objects -- + // objects need to have distinct addresses, so they need to have a size + // of at least one. But we can special case this situation. + const std::size_t size = + (std::is_same>::value ? 0 : sizeof(T)); + T object; (void)allow_compression; - Assert(std::distance(cbegin, cend) == sizeof(T), ExcInternalError()); - std::memcpy(&object, &*cbegin, sizeof(T)); + Assert(std::distance(cbegin, cend) == size, ExcInternalError()); + + if (size > 0) + std::memcpy(&object, &*cbegin, size); return object; } diff --git a/tests/base/utilities_pack_unpack_08.cc b/tests/base/utilities_pack_unpack_08.cc new file mode 100644 index 0000000000..6cf3a3e693 --- /dev/null +++ b/tests/base/utilities_pack_unpack_08.cc @@ -0,0 +1,60 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2017 - 2020 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, redistribute +// it, and/or modify it under the terms of the GNU Lesser General +// Public License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE.md at +// the top level directory of deal.II. +// +// --------------------------------------------------------------------- + + +// Make sure that Utilities::pack/unpack can be used on objects that +// are empty, and that the resulting packed string has length zero. + + +#include +#include +#include + +#include + +#include "../tests.h" + + + +void +test() +{ + using Empty = std::tuple<>; + + Empty e; + deallog << "Size = " << sizeof(e) << std::endl; + + // Pack the object and make sure the packed length is zero + const std::vector packed = Utilities::pack(e, false); + deallog << "Packed size = " << packed.size() << std::endl; + + // Then unpack again. The two should be the same -- though one may + // question what equality of objects of size zero might mean -- and + // we should check so: + Empty e2 = Utilities::unpack(packed, false); + Assert(e2 == e, ExcInternalError()); + + deallog << "OK" << std::endl; +} + + + +int +main() +{ + initlog(); + + test(); +} diff --git a/tests/base/utilities_pack_unpack_08.output b/tests/base/utilities_pack_unpack_08.output new file mode 100644 index 0000000000..e4ada5254e --- /dev/null +++ b/tests/base/utilities_pack_unpack_08.output @@ -0,0 +1,4 @@ + +DEAL::Size = 1 +DEAL::Packed size = 0 +DEAL::OK -- 2.39.5