diff options
author | Abseil Team <absl-team@google.com> | 2023-02-02 00:48:53 -0800 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-02-02 00:49:39 -0800 |
commit | 9858e5421b14c9d72f2103af10010c5452c6d55f (patch) | |
tree | af3a6297b0af2933fe1e4a7755b4214848c8e3c5 /absl/time/time.h | |
parent | 7c7cafef4ff11979a26702080a2a4bf0dad7d856 (diff) |
Get rid of tail padding within `absl::Duration`. This reduces memory usage needs when storing duration in containers (e.g. `vector<absl::Duration>` uses 25% less memory), and allows classes with `absl::Duration` fields to fit other stuff in memory previously used by tail padding (e.g. `std::optional<absl::Duration>` is now 16 bytes instead of 24).
PiperOrigin-RevId: 506543503
Change-Id: Ifeb2397c953a5d3da317a70ab49a3ebb85042344
Diffstat (limited to 'absl/time/time.h')
-rw-r--r-- | absl/time/time.h | 73 |
1 files changed, 70 insertions, 3 deletions
diff --git a/absl/time/time.h b/absl/time/time.h index cc390082..bf1007c2 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -84,6 +84,7 @@ struct timeval; #include <type_traits> #include <utility> +#include "absl/base/config.h" #include "absl/base/macros.h" #include "absl/strings/string_view.h" #include "absl/time/civil_time.h" @@ -214,7 +215,7 @@ class Duration { template <typename H> friend H AbslHashValue(H h, Duration d) { - return H::combine(std::move(h), d.rep_hi_, d.rep_lo_); + return H::combine(std::move(h), d.rep_hi_.Get(), d.rep_lo_); } private: @@ -223,7 +224,73 @@ class Duration { friend constexpr Duration time_internal::MakeDuration(int64_t hi, uint32_t lo); constexpr Duration(int64_t hi, uint32_t lo) : rep_hi_(hi), rep_lo_(lo) {} - int64_t rep_hi_; + + // We store `hi_rep_` 4-byte rather than 8-byte aligned to avoid 4 bytes of + // tail padding. + class HiRep { + public: + // Default constructor default-initializes `hi_`, which has the same + // semantics as default-initializing an `int64_t` (undetermined value). + HiRep() = default; + + HiRep(const HiRep&) = default; + HiRep& operator=(const HiRep&) = default; + + explicit constexpr HiRep(const int64_t value) + : // C++17 forbids default-initialization in constexpr contexts. We can + // remove this in C++20. + lo_(0), + hi_(0) { + *this = value; + } + + constexpr int64_t Get() const { + const uint64_t unsigned_value = + (static_cast<uint64_t>(hi_) << 32) | static_cast<uint64_t>(lo_); + // `static_cast<int64_t>(unsigned_value)` is implementation-defined + // before c++20. On all supported platforms the behaviour is that mandated + // by c++20, i.e. "If the destination type is signed, [...] the result is + // the unique value of the destination type equal to the source value + // modulo 2^n, where n is the number of bits used to represent the + // destination type." + static_assert( + (static_cast<int64_t>((std::numeric_limits<uint64_t>::max)()) == + int64_t{-1}) && + (static_cast<int64_t>(static_cast<uint64_t>( + (std::numeric_limits<int64_t>::max)()) + + 1) == + (std::numeric_limits<int64_t>::min)()), + "static_cast<int64_t>(uint64_t) does not have c++20 semantics"); + return static_cast<int64_t>(unsigned_value); + } + + constexpr HiRep& operator=(const int64_t value) { + // "If the destination type is unsigned, the resulting value is the + // smallest unsigned value equal to the source value modulo 2^n + // where `n` is the number of bits used to represent the destination + // type". + const auto unsigned_value = static_cast<uint64_t>(value); + hi_ = static_cast<uint32_t>(unsigned_value >> 32); + lo_ = static_cast<uint32_t>(unsigned_value); + return *this; + } + + private: + // Notes: + // - Ideally we would use a `char[]` and `std::bitcast`, but the latter + // does not exist (and is not constexpr in `absl`) before c++20. + // - Order is optimized depending on endianness so that the compiler can + // turn `Get()` (resp. `operator=()`) into a single 8-byte load (resp. + // store). +#if defined(ABSL_IS_BIG_ENDIAN) && ABSL_IS_BIG_ENDIAN + uint32_t hi_; + uint32_t lo_; +#else + uint32_t lo_; + uint32_t hi_; +#endif + }; + HiRep rep_hi_; uint32_t rep_lo_; }; @@ -1491,7 +1558,7 @@ ABSL_ATTRIBUTE_CONST_FUNCTION constexpr Duration MakeNormalizedDuration( // Provide access to the Duration representation. ABSL_ATTRIBUTE_CONST_FUNCTION constexpr int64_t GetRepHi(Duration d) { - return d.rep_hi_; + return d.rep_hi_.Get(); } ABSL_ATTRIBUTE_CONST_FUNCTION constexpr uint32_t GetRepLo(Duration d) { return d.rep_lo_; |