Skip to content

Commit 40cd2cf

Browse files
ezbrcopybara-github
authored andcommitted
More type-erasing of InitializeSlots by removing the Alloc and AlignOfSlot template parameters.
- Add policy.alloc as a type-erased function. - Share more usage of DeallocateBackingArray. Note that this requires calling infoz().Unregister() explicitly when needed. - Remove DeallocateStandard since it isn't needed anymore now that we got rid of dealloc_fn. - Add slot_align into PolicyFunctions, and use uint32_t instead of size_t for this and for slot_size. PiperOrigin-RevId: 724388818 Change-Id: I919cde94de63edef2c0f698fc8b55b9755cf9fc0
1 parent 5852b47 commit 40cd2cf

File tree

2 files changed

+78
-78
lines changed

2 files changed

+78
-78
lines changed

absl/container/internal/raw_hash_set.cc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size) {
337337
}
338338

339339
void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
340-
bool reuse, bool soo_enabled) {
340+
void* alloc, bool reuse, bool soo_enabled) {
341341
c.set_size(0);
342342
if (reuse) {
343343
assert(!soo_enabled || c.capacity() > SooCapacity());
@@ -349,7 +349,9 @@ void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
349349
// infoz.
350350
c.infoz().RecordClearedReservation();
351351
c.infoz().RecordStorageChanged(0, soo_enabled ? SooCapacity() : 0);
352-
(*policy.dealloc)(c, policy);
352+
c.infoz().Unregister();
353+
(*policy.dealloc)(alloc, c.capacity(), c.control(), policy.slot_size,
354+
policy.slot_align, c.has_infoz());
353355
c = soo_enabled ? CommonFields{soo_tag_t{}} : CommonFields{non_soo_tag_t{}};
354356
}
355357
}

absl/container/internal/raw_hash_set.h

Lines changed: 74 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1976,12 +1976,11 @@ constexpr bool ShouldSampleHashtablezInfoForAlloc() {
19761976
return std::is_same<CharAlloc, std::allocator<char>>::value;
19771977
}
19781978

1979-
template <typename CharAlloc, bool kSooEnabled>
1979+
template <bool kSooEnabled>
19801980
bool ShouldSampleHashtablezInfoOnResize(bool force_sampling,
1981+
bool is_hashtablez_eligible,
19811982
size_t old_capacity, CommonFields& c) {
1982-
if (!ShouldSampleHashtablezInfoForAlloc<CharAlloc>()) {
1983-
return false;
1984-
}
1983+
if (!is_hashtablez_eligible) return false;
19851984
// Force sampling is only allowed for SOO tables.
19861985
ABSL_SWISSTABLE_ASSERT(kSooEnabled || !force_sampling);
19871986
if (kSooEnabled && force_sampling) {
@@ -1998,12 +1997,32 @@ bool ShouldSampleHashtablezInfoOnResize(bool force_sampling,
19981997
return false;
19991998
}
20001999

2000+
// Allocates `n` bytes for a backing array.
2001+
template <size_t AlignOfBackingArray, typename CharAlloc>
2002+
ABSL_ATTRIBUTE_NOINLINE void* AllocateBackingArray(void* alloc, size_t n) {
2003+
return Allocate<AlignOfBackingArray>(static_cast<CharAlloc*>(alloc), n);
2004+
}
2005+
2006+
template <size_t AlignOfBackingArray, typename CharAlloc>
2007+
ABSL_ATTRIBUTE_NOINLINE void DeallocateBackingArray(
2008+
void* alloc, size_t capacity, ctrl_t* ctrl, size_t slot_size,
2009+
size_t slot_align, bool had_infoz) {
2010+
RawHashSetLayout layout(capacity, slot_align, had_infoz);
2011+
void* backing_array = ctrl - layout.control_offset();
2012+
const size_t alloc_size = layout.alloc_size(slot_size);
2013+
// Unpoison before returning the memory to the allocator.
2014+
SanitizerUnpoisonMemoryRegion(backing_array, alloc_size);
2015+
Deallocate<AlignOfBackingArray>(static_cast<CharAlloc*>(alloc), backing_array,
2016+
alloc_size);
2017+
}
2018+
20012019
// PolicyFunctions bundles together some information for a particular
20022020
// raw_hash_set<T, ...> instantiation. This information is passed to
20032021
// type-erased functions that want to do small amounts of type-specific
20042022
// work.
20052023
struct PolicyFunctions {
2006-
size_t slot_size;
2024+
uint32_t slot_size;
2025+
uint32_t slot_align;
20072026

20082027
// Returns the pointer to the hash function stored in the set.
20092028
const void* (*hash_fn)(const CommonFields& common);
@@ -2014,8 +2033,12 @@ struct PolicyFunctions {
20142033
// Transfers the contents of src_slot to dst_slot.
20152034
void (*transfer)(void* set, void* dst_slot, void* src_slot);
20162035

2036+
// Allocates n bytes for the backing store for common.
2037+
void* (*alloc)(void* alloc, size_t n);
2038+
20172039
// Deallocates the backing store from common.
2018-
void (*dealloc)(CommonFields& common, const PolicyFunctions& policy);
2040+
void (*dealloc)(void* alloc, size_t capacity, ctrl_t* ctrl, size_t slot_size,
2041+
size_t slot_align, bool had_infoz);
20192042

20202043
// Resizes set to the new capacity.
20212044
// Arguments are used as in raw_hash_set::resize_impl.
@@ -2116,12 +2139,13 @@ constexpr size_t OptimalMemcpySizeForSooSlotTransfer(bool transfer_uses_memcpy,
21162139
class HashSetResizeHelper {
21172140
public:
21182141
explicit HashSetResizeHelper(CommonFields& c, bool was_soo, bool had_soo_slot,
2119-
bool force_infoz)
2142+
bool force_infoz, bool is_hashtablez_eligible)
21202143
: old_capacity_(c.capacity()),
21212144
had_infoz_(c.has_infoz()),
21222145
was_soo_(was_soo),
21232146
had_soo_slot_(had_soo_slot),
2124-
force_infoz_(force_infoz) {}
2147+
force_infoz_(force_infoz),
2148+
is_hashtablez_eligible_(is_hashtablez_eligible) {}
21252149

21262150
// Optimized for small groups version of `find_first_non_full`.
21272151
// Beneficial only right after calling `raw_hash_set::resize`.
@@ -2177,31 +2201,30 @@ class HashSetResizeHelper {
21772201
// infoz.RecordRehash is called if old_capacity == 0.
21782202
//
21792203
// Returns IsGrowingIntoSingleGroupApplicable result to avoid recomputation.
2180-
template <typename Alloc,
2181-
// The size we are allowed to copy to transfer SOO slot to
2182-
// SooSlotIndex(). See OptimalMemcpySizeForSooSlotTransfer().
2183-
size_t SooSlotMemcpySize, bool TransferUsesMemcpy, bool SooEnabled,
2184-
size_t AlignOfSlot>
2185-
ABSL_ATTRIBUTE_NOINLINE bool InitializeSlots(CommonFields& c, Alloc alloc,
2204+
//
2205+
// SooSlotMemcpySize is the size we are allowed to copy to transfer SOO slot
2206+
// to SooSlotIndex(). See OptimalMemcpySizeForSooSlotTransfer().
2207+
template <size_t SooSlotMemcpySize, bool TransferUsesMemcpy, bool SooEnabled>
2208+
ABSL_ATTRIBUTE_NOINLINE bool InitializeSlots(CommonFields& c, void* alloc,
21862209
size_t soo_slot_hash,
21872210
size_t key_size,
21882211
size_t value_size,
21892212
const PolicyFunctions& policy) {
21902213
ABSL_SWISSTABLE_ASSERT(c.capacity());
21912214
const size_t slot_size = policy.slot_size;
2215+
const size_t slot_align = policy.slot_align;
21922216
HashtablezInfoHandle infoz = c.infoz();
2193-
const bool should_sample =
2194-
ShouldSampleHashtablezInfoOnResize<Alloc, SooEnabled>(force_infoz_,
2195-
old_capacity_, c);
2217+
const bool should_sample = ShouldSampleHashtablezInfoOnResize<SooEnabled>(
2218+
force_infoz_, is_hashtablez_eligible_, old_capacity_, c);
21962219
if (ABSL_PREDICT_FALSE(should_sample)) {
21972220
infoz = ForcedTrySample(slot_size, key_size, value_size,
21982221
SooEnabled ? SooCapacity() : 0);
21992222
}
22002223
const bool has_infoz = infoz.IsSampled();
22012224

2202-
RawHashSetLayout layout(c.capacity(), AlignOfSlot, has_infoz);
2203-
char* mem = static_cast<char*>(Allocate<BackingArrayAlignment(AlignOfSlot)>(
2204-
&alloc, layout.alloc_size(slot_size)));
2225+
RawHashSetLayout layout(c.capacity(), slot_align, has_infoz);
2226+
char* mem =
2227+
static_cast<char*>(policy.alloc(alloc, layout.alloc_size(slot_size)));
22052228
const GenerationType old_generation = c.generation();
22062229
c.set_generation_ptr(
22072230
reinterpret_cast<GenerationType*>(mem + layout.generation_offset()));
@@ -2260,7 +2283,8 @@ class HashSetResizeHelper {
22602283
if ((SooEnabled || old_capacity_ != 0) && grow_single_group) {
22612284
if (TransferUsesMemcpy) {
22622285
GrowSizeIntoSingleGroupTransferable(c, slot_size);
2263-
DeallocateOld<AlignOfSlot>(alloc, slot_size);
2286+
(*policy.dealloc)(alloc, old_capacity_, old_ctrl(), slot_size,
2287+
slot_align, had_infoz_);
22642288
} else {
22652289
GrowIntoSingleGroupShuffleControlBytes(c.control(),
22662290
layout.capacity());
@@ -2312,12 +2336,9 @@ class HashSetResizeHelper {
23122336

23132337
// Deallocates old backing array.
23142338
template <size_t AlignOfSlot, class CharAlloc>
2315-
void DeallocateOld(CharAlloc alloc_ref, size_t slot_size) {
2316-
SanitizerUnpoisonMemoryRegion(old_slots(), slot_size * old_capacity_);
2317-
auto layout = RawHashSetLayout(old_capacity_, AlignOfSlot, had_infoz_);
2318-
Deallocate<BackingArrayAlignment(AlignOfSlot)>(
2319-
&alloc_ref, old_ctrl() - layout.control_offset(),
2320-
layout.alloc_size(slot_size));
2339+
void DeallocateOld(CharAlloc alloc, size_t slot_size) {
2340+
DeallocateBackingArray<BackingArrayAlignment(AlignOfSlot), CharAlloc>(
2341+
&alloc, old_capacity_, old_ctrl(), slot_size, AlignOfSlot, had_infoz_);
23212342
}
23222343

23232344
private:
@@ -2436,6 +2457,7 @@ class HashSetResizeHelper {
24362457
// True if it is known that table needs to be sampled.
24372458
// Used for sampling SOO tables.
24382459
bool force_infoz_;
2460+
bool is_hashtablez_eligible_;
24392461
};
24402462

24412463
inline void PrepareInsertCommon(CommonFields& common) {
@@ -2451,29 +2473,11 @@ size_t PrepareInsertAfterSoo(size_t hash, size_t slot_size,
24512473
// or creating a new one based on the value of "reuse".
24522474
// REQUIRES: c.capacity > 0
24532475
void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
2454-
bool reuse, bool soo_enabled);
2476+
void* alloc, bool reuse, bool soo_enabled);
24552477

24562478
// Type-erased version of raw_hash_set::erase_meta_only.
24572479
void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size);
24582480

2459-
// Function to place in PolicyFunctions::dealloc for raw_hash_sets
2460-
// that are using std::allocator. This allows us to share the same
2461-
// function body for raw_hash_set instantiations that have the
2462-
// same slot alignment.
2463-
template <size_t AlignOfSlot>
2464-
ABSL_ATTRIBUTE_NOINLINE void DeallocateStandard(CommonFields& common,
2465-
const PolicyFunctions& policy) {
2466-
// Unpoison before returning the memory to the allocator.
2467-
SanitizerUnpoisonMemoryRegion(common.slot_array(),
2468-
policy.slot_size * common.capacity());
2469-
2470-
std::allocator<char> alloc;
2471-
common.infoz().Unregister();
2472-
Deallocate<BackingArrayAlignment(AlignOfSlot)>(
2473-
&alloc, common.backing_array_start(),
2474-
common.alloc_size(policy.slot_size, AlignOfSlot));
2475-
}
2476-
24772481
// For trivially relocatable types we use memcpy directly. This allows us to
24782482
// share the same function body for raw_hash_set instantiations that have the
24792483
// same slot size as long as they are relocatable.
@@ -3802,7 +3806,9 @@ class raw_hash_set {
38023806

38033807
void clear_backing_array(bool reuse) {
38043808
ABSL_SWISSTABLE_ASSERT(capacity() > DefaultCapacity());
3805-
ClearBackingArray(common(), GetPolicyFunctions(), reuse, SooEnabled());
3809+
CharAlloc alloc(alloc_ref());
3810+
ClearBackingArray(common(), GetPolicyFunctions(), &alloc, reuse,
3811+
SooEnabled());
38063812
}
38073813

38083814
void destroy_slots() {
@@ -3819,9 +3825,11 @@ class raw_hash_set {
38193825
// Unpoison before returning the memory to the allocator.
38203826
SanitizerUnpoisonMemoryRegion(slot_array(), sizeof(slot_type) * capacity());
38213827
infoz().Unregister();
3822-
Deallocate<BackingArrayAlignment(alignof(slot_type))>(
3823-
&alloc_ref(), common().backing_array_start(),
3824-
common().alloc_size(sizeof(slot_type), alignof(slot_type)));
3828+
CharAlloc alloc(alloc_ref());
3829+
DeallocateBackingArray<BackingArrayAlignment(alignof(slot_type)),
3830+
CharAlloc>(&alloc, capacity(), control(),
3831+
sizeof(slot_type), alignof(slot_type),
3832+
common().has_infoz());
38253833
}
38263834

38273835
void destructor_impl() {
@@ -3887,21 +3895,22 @@ class raw_hash_set {
38873895
const bool had_soo_slot = was_soo && !set->empty();
38883896
const size_t soo_slot_hash =
38893897
had_soo_slot ? set->hash_of(set->soo_slot()) : 0;
3890-
HashSetResizeHelper resize_helper(common, was_soo, had_soo_slot,
3891-
force_infoz);
3898+
HashSetResizeHelper resize_helper(
3899+
common, was_soo, had_soo_slot, force_infoz,
3900+
ShouldSampleHashtablezInfoForAlloc<CharAlloc>());
38923901
common.set_capacity(new_capacity);
38933902
// Note that `InitializeSlots` does different number initialization steps
38943903
// depending on the values of `transfer_uses_memcpy` and capacities.
38953904
// Refer to the comment in `InitializeSlots` for more details.
3905+
CharAlloc alloc(set->alloc_ref());
38963906
const bool grow_single_group =
3897-
resize_helper.InitializeSlots<CharAlloc,
3898-
OptimalMemcpySizeForSooSlotTransfer(
3907+
resize_helper.InitializeSlots<OptimalMemcpySizeForSooSlotTransfer(
38993908
PolicyTraits::transfer_uses_memcpy(),
39003909
SooEnabled(), sizeof(slot_type)),
39013910
PolicyTraits::transfer_uses_memcpy(),
3902-
SooEnabled(), alignof(slot_type)>(
3903-
common, CharAlloc(set->alloc_ref()), soo_slot_hash,
3904-
sizeof(key_type), sizeof(value_type), GetPolicyFunctions());
3911+
SooEnabled()>(
3912+
common, &alloc, soo_slot_hash, sizeof(key_type), sizeof(value_type),
3913+
GetPolicyFunctions());
39053914

39063915
if (resize_helper.old_capacity() == DefaultCapacity()) {
39073916
// InitializeSlots did all the work including infoz().RecordRehash().
@@ -4288,23 +4297,14 @@ class raw_hash_set {
42884297
auto* h = static_cast<raw_hash_set*>(set);
42894298
h->transfer(static_cast<slot_type*>(dst), static_cast<slot_type*>(src));
42904299
}
4291-
// Note: dealloc_fn will only be used if we have a non-standard allocator.
4292-
static void dealloc_fn(CommonFields& common, const PolicyFunctions&) {
4293-
auto* set = reinterpret_cast<raw_hash_set*>(&common);
4294-
4295-
// Unpoison before returning the memory to the allocator.
4296-
SanitizerUnpoisonMemoryRegion(common.slot_array(),
4297-
sizeof(slot_type) * common.capacity());
4298-
4299-
common.infoz().Unregister();
4300-
Deallocate<BackingArrayAlignment(alignof(slot_type))>(
4301-
&set->alloc_ref(), common.backing_array_start(),
4302-
common.alloc_size(sizeof(slot_type), alignof(slot_type)));
4303-
}
43044300

43054301
static const PolicyFunctions& GetPolicyFunctions() {
4302+
static_assert(sizeof(slot_type) <= (std::numeric_limits<uint32_t>::max)());
4303+
static_assert(alignof(slot_type) <= (std::numeric_limits<uint32_t>::max)());
4304+
static constexpr size_t kBackingArrayAlignment =
4305+
BackingArrayAlignment(alignof(slot_type));
43064306
static constexpr PolicyFunctions value = {
4307-
sizeof(slot_type),
4307+
sizeof(slot_type), alignof(slot_type),
43084308
// TODO(b/328722020): try to type erase
43094309
// for standard layout and alignof(Hash) <= alignof(CommonFields).
43104310
std::is_empty<hasher>::value ? &GetHashRefForEmptyHasher
@@ -4313,11 +4313,9 @@ class raw_hash_set {
43134313
PolicyTraits::transfer_uses_memcpy()
43144314
? TransferRelocatable<sizeof(slot_type)>
43154315
: &raw_hash_set::transfer_slot_fn,
4316-
(std::is_same<SlotAlloc, std::allocator<slot_type>>::value
4317-
? &DeallocateStandard<alignof(slot_type)>
4318-
: &raw_hash_set::dealloc_fn),
4319-
&raw_hash_set::resize_impl
4320-
};
4316+
&AllocateBackingArray<kBackingArrayAlignment, CharAlloc>,
4317+
&DeallocateBackingArray<kBackingArrayAlignment, CharAlloc>,
4318+
&raw_hash_set::resize_impl};
43214319
return value;
43224320
}
43234321

0 commit comments

Comments
 (0)