Do not reserve space for GrowthInfo for single element tables in non-SOO case.

That would save 8 bytes of allocation size for such tables.

PiperOrigin-RevId: 918340255
Change-Id: Ic5f00dfb87392089ac04242418e4f55cc599619e
This commit is contained in:
Vitaly Goldshteyn
2026-05-20 03:22:32 -07:00
committed by Copybara-Service
parent 8fb4507d10
commit c508bab517
3 changed files with 39 additions and 13 deletions

View File

@@ -822,11 +822,15 @@ void ResizeNonSooImpl(CommonFields& common,
common, policy, old_ctrl, old_slots, old_capacity);
(*policy.dealloc)(alloc, old_capacity, old_ctrl, slot_size, slot_align,
has_infoz);
ResetGrowthLeft(GetGrowthInfoFromControl(new_ctrl), new_capacity,
common.size());
if (HasGrowthInfoForCapacity(new_capacity)) {
ResetGrowthLeft(GetGrowthInfoFromControl(new_ctrl), new_capacity,
common.size());
}
} else {
GetGrowthInfoFromControl(new_ctrl).InitGrowthLeftNoDeleted(
CapacityToGrowth(new_capacity));
if (HasGrowthInfoForCapacity(new_capacity)) {
GetGrowthInfoFromControl(new_ctrl).InitGrowthLeftNoDeleted(
CapacityToGrowth(new_capacity));
}
}
if (ABSL_PREDICT_FALSE(has_infoz)) {
@@ -1215,7 +1219,8 @@ class ProbedItemEncoder {
ProbedItem* OverflowBufferStart() const {
// We reuse GrowthInfo memory as well.
return AlignToNextItem(control_ - ControlOffset(/*has_infoz=*/false));
return AlignToNextItem(control_ - ControlOffset(/*has_infoz=*/false,
/*has_growth_info=*/true));
}
// Encodes item when previously allocated buffer is full.

View File

@@ -989,10 +989,20 @@ constexpr size_t NumControlBytes(size_t capacity) {
return IsSmallCapacity(capacity) ? 0 : capacity + 1 + NumClonedBytes();
}
// Returns whether table with the given capacity has a GrowthInfo.
constexpr bool HasGrowthInfoForCapacity(size_t capacity) {
return !IsSmallCapacity(capacity);
}
// Computes the offset from the start of the backing allocation of control.
// infoz and growth_info are stored at the beginning of the backing array.
constexpr size_t ControlOffset(bool has_infoz) {
return (has_infoz ? sizeof(HashtablezInfoHandle) : 0) + sizeof(GrowthInfo);
constexpr size_t ControlOffset(bool has_infoz, bool has_growth_info) {
if (ABSL_PREDICT_FALSE(has_infoz)) {
// We always allocate GrowthInfo for sampled tables to allow branchless
// access to infoz pointer.
return sizeof(HashtablezInfoHandle) + sizeof(GrowthInfo);
}
return has_growth_info ? sizeof(GrowthInfo) : 0;
}
// Returns the offset of the next item after `offset` that is aligned to `align`
@@ -1004,12 +1014,10 @@ constexpr size_t AlignUpTo(size_t offset, size_t align) {
// Helper class for computing offsets and allocation size of hash set fields.
class RawHashSetLayout {
public:
// TODO(b/413062340): maybe don't allocate growth info for capacity 1 tables.
// Doing so may require additional branches/complexity so it might not be
// worth it.
explicit RawHashSetLayout(size_t capacity, size_t slot_size,
size_t slot_align, bool has_infoz)
: control_offset_(ControlOffset(has_infoz)),
: control_offset_(
ControlOffset(has_infoz, HasGrowthInfoForCapacity(capacity))),
generation_offset_(control_offset_ + NumControlBytes(capacity)),
slot_offset_(
AlignUpTo(generation_offset_ + NumGenerationBytes(), slot_align)),
@@ -1240,7 +1248,7 @@ class CommonFields : public CommonFieldsGenerationInfo {
size_t growth_left() const { return growth_info().GetGrowthLeft(); }
GrowthInfo& growth_info() {
ABSL_SWISSTABLE_ASSERT(!is_small());
ABSL_SWISSTABLE_ASSERT(HasGrowthInfoForCapacity(capacity()));
return GetGrowthInfoFromControl(control());
}
GrowthInfo growth_info() const {
@@ -1259,7 +1267,8 @@ class CommonFields : public CommonFieldsGenerationInfo {
reinterpret_cast<uintptr_t>(control()) % alignof(size_t) == 0);
ABSL_SWISSTABLE_ASSERT(has_infoz());
return reinterpret_cast<HashtablezInfoHandle*>(
control() - ControlOffset(/*has_infoz=*/true));
control() - ControlOffset(/*has_infoz=*/true,
HasGrowthInfoForCapacity(capacity())));
}
HashtablezInfoHandle infoz() {

View File

@@ -2772,6 +2772,18 @@ TYPED_TEST(SooTest, HintInsert) {
EXPECT_TRUE(node); // NOLINT(bugprone-use-after-move)
}
TYPED_TEST(SooTest, RehashZeroForSmallTable) {
TypeParam t{0};
EXPECT_EQ(t.capacity(), 1);
t.rehash(0);
EXPECT_EQ(t.capacity(), 1);
EXPECT_TRUE(t.contains(0));
t.insert(1);
EXPECT_EQ(t.capacity(), NextCapacity(1));
EXPECT_TRUE(t.contains(0));
EXPECT_TRUE(t.contains(1));
}
template <typename T>
T MakeSimpleTable(size_t size, bool do_reserve) {
T t;