diff options
author | Abseil Team <absl-team@google.com> | 2023-02-02 03:05:07 -0800 |
---|---|---|
committer | Copybara-Service <copybara-worker@google.com> | 2023-02-02 03:05:54 -0800 |
commit | 7005fede1e309d0a6070be3008b365112492aa80 (patch) | |
tree | 99968e37829bea518b344d13e286bd54508aa656 | |
parent | 9858e5421b14c9d72f2103af10010c5452c6d55f (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: 506568782
Change-Id: Ic9e077f02a80da013fb2d312aff77761b970c07a
-rw-r--r-- | absl/time/duration.cc | 39 | ||||
-rw-r--r-- | absl/time/duration_test.cc | 10 | ||||
-rw-r--r-- | absl/time/time.h | 73 |
3 files changed, 21 insertions, 101 deletions
diff --git a/absl/time/duration.cc b/absl/time/duration.cc index b915d749..911e80f8 100644 --- a/absl/time/duration.cc +++ b/absl/time/duration.cc @@ -407,18 +407,16 @@ int64_t IDivDuration(bool satq, const Duration num, const Duration den, Duration& Duration::operator+=(Duration rhs) { if (time_internal::IsInfiniteDuration(*this)) return *this; if (time_internal::IsInfiniteDuration(rhs)) return *this = rhs; - const int64_t orig_rep_hi = rep_hi_.Get(); - rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) + - EncodeTwosComp(rhs.rep_hi_.Get())); + const int64_t orig_rep_hi = rep_hi_; + rep_hi_ = + DecodeTwosComp(EncodeTwosComp(rep_hi_) + EncodeTwosComp(rhs.rep_hi_)); if (rep_lo_ >= kTicksPerSecond - rhs.rep_lo_) { - rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) + 1); + rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_) + 1); rep_lo_ -= kTicksPerSecond; } rep_lo_ += rhs.rep_lo_; - if (rhs.rep_hi_.Get() < 0 ? rep_hi_.Get() > orig_rep_hi - : rep_hi_.Get() < orig_rep_hi) { - return *this = - rhs.rep_hi_.Get() < 0 ? -InfiniteDuration() : InfiniteDuration(); + if (rhs.rep_hi_ < 0 ? rep_hi_ > orig_rep_hi : rep_hi_ < orig_rep_hi) { + return *this = rhs.rep_hi_ < 0 ? -InfiniteDuration() : InfiniteDuration(); } return *this; } @@ -426,21 +424,18 @@ Duration& Duration::operator+=(Duration rhs) { Duration& Duration::operator-=(Duration rhs) { if (time_internal::IsInfiniteDuration(*this)) return *this; if (time_internal::IsInfiniteDuration(rhs)) { - return *this = rhs.rep_hi_.Get() >= 0 ? -InfiniteDuration() - : InfiniteDuration(); + return *this = rhs.rep_hi_ >= 0 ? -InfiniteDuration() : InfiniteDuration(); } - const int64_t orig_rep_hi = rep_hi_.Get(); - rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) - - EncodeTwosComp(rhs.rep_hi_.Get())); + const int64_t orig_rep_hi = rep_hi_; + rep_hi_ = + DecodeTwosComp(EncodeTwosComp(rep_hi_) - EncodeTwosComp(rhs.rep_hi_)); if (rep_lo_ < rhs.rep_lo_) { - rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_.Get()) - 1); + rep_hi_ = DecodeTwosComp(EncodeTwosComp(rep_hi_) - 1); rep_lo_ += kTicksPerSecond; } rep_lo_ -= rhs.rep_lo_; - if (rhs.rep_hi_.Get() < 0 ? rep_hi_.Get() < orig_rep_hi - : rep_hi_.Get() > orig_rep_hi) { - return *this = rhs.rep_hi_.Get() >= 0 ? -InfiniteDuration() - : InfiniteDuration(); + if (rhs.rep_hi_ < 0 ? rep_hi_ < orig_rep_hi : rep_hi_ > orig_rep_hi) { + return *this = rhs.rep_hi_ >= 0 ? -InfiniteDuration() : InfiniteDuration(); } return *this; } @@ -451,7 +446,7 @@ Duration& Duration::operator-=(Duration rhs) { Duration& Duration::operator*=(int64_t r) { if (time_internal::IsInfiniteDuration(*this)) { - const bool is_neg = (r < 0) != (rep_hi_.Get() < 0); + const bool is_neg = (r < 0) != (rep_hi_ < 0); return *this = is_neg ? -InfiniteDuration() : InfiniteDuration(); } return *this = ScaleFixed<SafeMultiply>(*this, r); @@ -459,7 +454,7 @@ Duration& Duration::operator*=(int64_t r) { Duration& Duration::operator*=(double r) { if (time_internal::IsInfiniteDuration(*this) || !IsFinite(r)) { - const bool is_neg = std::signbit(r) != (rep_hi_.Get() < 0); + const bool is_neg = (std::signbit(r) != 0) != (rep_hi_ < 0); return *this = is_neg ? -InfiniteDuration() : InfiniteDuration(); } return *this = ScaleDouble<std::multiplies>(*this, r); @@ -467,7 +462,7 @@ Duration& Duration::operator*=(double r) { Duration& Duration::operator/=(int64_t r) { if (time_internal::IsInfiniteDuration(*this) || r == 0) { - const bool is_neg = (r < 0) != (rep_hi_.Get() < 0); + const bool is_neg = (r < 0) != (rep_hi_ < 0); return *this = is_neg ? -InfiniteDuration() : InfiniteDuration(); } return *this = ScaleFixed<std::divides>(*this, r); @@ -475,7 +470,7 @@ Duration& Duration::operator/=(int64_t r) { Duration& Duration::operator/=(double r) { if (time_internal::IsInfiniteDuration(*this) || !IsValidDivisor(r)) { - const bool is_neg = std::signbit(r) != (rep_hi_.Get() < 0); + const bool is_neg = (std::signbit(r) != 0) != (rep_hi_ < 0); return *this = is_neg ? -InfiniteDuration() : InfiniteDuration(); } return *this = ScaleDouble<std::divides>(*this, r); diff --git a/absl/time/duration_test.cc b/absl/time/duration_test.cc index 84224f47..b7abf4ba 100644 --- a/absl/time/duration_test.cc +++ b/absl/time/duration_test.cc @@ -16,9 +16,8 @@ #include <winsock2.h> // for timeval #endif -#include <array> -#include <cfloat> #include <chrono> // NOLINT(build/c++11) +#include <cfloat> #include <cmath> #include <cstdint> #include <ctime> @@ -1854,11 +1853,4 @@ TEST(Duration, FormatParseRoundTrip) { #undef TEST_PARSE_ROUNDTRIP } -TEST(Duration, NoPadding) { - // Should match the size of a struct with uint32_t alignment and no padding. - using NoPadding = std::array<uint32_t, 3>; - EXPECT_EQ(sizeof(NoPadding), sizeof(absl::Duration)); - EXPECT_EQ(alignof(NoPadding), alignof(absl::Duration)); -} - } // namespace diff --git a/absl/time/time.h b/absl/time/time.h index bf1007c2..cc390082 100644 --- a/absl/time/time.h +++ b/absl/time/time.h @@ -84,7 +84,6 @@ 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" @@ -215,7 +214,7 @@ class Duration { template <typename H> friend H AbslHashValue(H h, Duration d) { - return H::combine(std::move(h), d.rep_hi_.Get(), d.rep_lo_); + return H::combine(std::move(h), d.rep_hi_, d.rep_lo_); } private: @@ -224,73 +223,7 @@ 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) {} - - // 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_; + int64_t rep_hi_; uint32_t rep_lo_; }; @@ -1558,7 +1491,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_.Get(); + return d.rep_hi_; } ABSL_ATTRIBUTE_CONST_FUNCTION constexpr uint32_t GetRepLo(Duration d) { return d.rep_lo_; |