Skip to content

Commit 2844cda

Browse files
goldvitalycopybara-github
authored andcommitted
Introduce and use SetCtrlInLargeTable, when we know that table is at least one group. Similarly to SetCtrlInSingleGroupTable, we can save some operations.
https://gcc.godbolt.org/z/PjrzxbMz7 We are saving ~3 microops that is not big, but still positive. Currently the function is not used in the hot loop, but the plan is to use the function in the new implementation of `GrowToNextCapacityAndPrepareInsert`. So introducing and battle test the function in advance to simplify review iterations. Additionally adding "PrepareInsert" to "DropDeletesWithoutResize". That is done for consistency with `GrowToNextCapacityAndPrepareInsert` and to move more code to the colder function. Microbenchmarks: ``` name old INSTRUCTIONS/op new INSTRUCTIONS/op delta BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:16 179 ± 0% 179 ± 0% -0.29% (p=0.000 n=27+27) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:32 197 ± 0% 196 ± 0% -0.66% (p=0.000 n=27+27) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:64 206 ± 0% 204 ± 0% -0.68% (p=0.000 n=27+26) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:128 210 ± 0% 208 ± 0% -0.66% (p=0.000 n=25+27) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:256 212 ± 0% 210 ± 0% -0.67% (p=0.000 n=27+27) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:512 213 ± 0% 211 ± 0% -0.74% (p=0.000 n=27+26) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:1024 213 ± 0% 211 ± 0% -0.69% (p=0.000 n=27+27) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:2048 213 ± 0% 212 ± 0% -0.72% (p=0.000 n=27+27) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:4096 213 ± 0% 212 ± 0% -0.69% (p=0.000 n=27+27) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:8192 213 ± 0% 212 ± 0% -0.68% (p=0.000 n=27+26) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:16384 213 ± 0% 212 ± 0% -0.70% (p=0.000 n=27+27) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:32768 213 ± 0% 212 ± 0% -0.70% (p=0.000 n=27+27) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:65536 219 ± 1% 218 ± 0% -0.77% (p=0.000 n=27+26) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:131072 210 ± 0% 209 ± 0% -0.56% (p=0.000 n=27+25) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:262144 203 ± 0% 203 ± 0% -0.32% (p=0.000 n=27+27) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:524288 203 ± 0% 202 ± 0% -0.29% (p=0.000 n=25+26) BM_SWISSMAP_EraseInsert_Hot<::absl::flat_hash_set, 4>/set_size:1048576 202 ± 0% 201 ± 0% -0.20% (p=0.000 n=27+27) ``` PiperOrigin-RevId: 728988087 Change-Id: Ic63f45eed3b922c4255dc36077fcf9a3d9b8b5ec
1 parent dc1ec89 commit 2844cda

File tree

2 files changed

+34
-17
lines changed

2 files changed

+34
-17
lines changed

absl/container/internal/raw_hash_set.cc

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,9 @@ size_t FindFirstFullSlot(size_t start, size_t end, const ctrl_t* ctrl) {
259259
ABSL_UNREACHABLE();
260260
}
261261

262-
void DropDeletesWithoutResize(CommonFields& common,
263-
const PolicyFunctions& policy) {
262+
size_t DropDeletesWithoutResizeAndPrepareInsert(CommonFields& common,
263+
size_t new_hash,
264+
const PolicyFunctions& policy) {
264265
void* set = &common;
265266
void* slot_array = common.slot_array();
266267
const size_t capacity = common.capacity();
@@ -320,7 +321,7 @@ void DropDeletesWithoutResize(CommonFields& common,
320321

321322
// Element doesn't move.
322323
if (ABSL_PREDICT_TRUE(probe_index(new_i) == probe_index(i))) {
323-
SetCtrl(common, i, H2(hash), slot_size);
324+
SetCtrlInLargeTable(common, i, H2(hash), slot_size);
324325
continue;
325326
}
326327

@@ -329,14 +330,14 @@ void DropDeletesWithoutResize(CommonFields& common,
329330
// Transfer element to the empty spot.
330331
// SetCtrl poisons/unpoisons the slots so we have to call it at the
331332
// right time.
332-
SetCtrl(common, new_i, H2(hash), slot_size);
333+
SetCtrlInLargeTable(common, new_i, H2(hash), slot_size);
333334
(*transfer)(set, new_slot_ptr, slot_ptr, 1);
334-
SetCtrl(common, i, ctrl_t::kEmpty, slot_size);
335+
SetCtrlInLargeTable(common, i, ctrl_t::kEmpty, slot_size);
335336
// Initialize or change empty space id.
336337
tmp_space_id = i;
337338
} else {
338339
assert(IsDeleted(ctrl[new_i]));
339-
SetCtrl(common, new_i, H2(hash), slot_size);
340+
SetCtrlInLargeTable(common, new_i, H2(hash), slot_size);
340341
// Until we are done rehashing, DELETED marks previously FULL slots.
341342

342343
if (tmp_space_id == kUnknownId) {
@@ -357,8 +358,14 @@ void DropDeletesWithoutResize(CommonFields& common,
357358
slot_ptr = PrevSlot(slot_ptr, slot_size);
358359
}
359360
}
361+
// Prepare insert for the new element.
362+
PrepareInsertCommon(common);
360363
ResetGrowthLeft(common);
364+
FindInfo find_info = find_first_non_full(common, new_hash);
365+
SetCtrlInLargeTable(common, find_info.offset, H2(new_hash), policy.slot_size);
366+
common.infoz().RecordInsert(new_hash, find_info.probe_length);
361367
common.infoz().RecordRehash(total_probe_length);
368+
return find_info.offset;
362369
}
363370

364371
static bool WasNeverFull(CommonFields& c, size_t index) {
@@ -416,7 +423,7 @@ void EraseMetaOnly(CommonFields& c, size_t index, size_t slot_size) {
416423
}
417424

418425
c.growth_info().OverwriteFullAsDeleted();
419-
SetCtrl(c, index, ctrl_t::kDeleted, slot_size);
426+
SetCtrlInLargeTable(c, index, ctrl_t::kDeleted, slot_size);
420427
}
421428

422429
void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
@@ -802,7 +809,7 @@ size_t GrowToNextCapacityAndPrepareInsert(CommonFields& common, size_t new_hash,
802809
total_probe_length = policy.find_new_positions_and_transfer_slots(
803810
common, old_ctrl, old_slots, old_capacity);
804811
find_info = find_first_non_full(common, new_hash);
805-
SetCtrl(common, find_info.offset, new_h2, policy.slot_size);
812+
SetCtrlInLargeTable(common, find_info.offset, new_h2, policy.slot_size);
806813
}
807814
assert(old_capacity > policy.soo_capacity);
808815
(*policy.dealloc)(alloc, old_capacity, old_ctrl, slot_size, slot_align,
@@ -825,7 +832,7 @@ size_t GrowToNextCapacityAndPrepareInsert(CommonFields& common, size_t new_hash,
825832
// tombstones via rehash or growth to next capacity.
826833
ABSL_ATTRIBUTE_NOINLINE
827834
size_t RehashOrGrowToNextCapacityAndPrepareInsert(
828-
CommonFields& common, size_t hash, const PolicyFunctions& policy) {
835+
CommonFields& common, size_t new_hash, const PolicyFunctions& policy) {
829836
const size_t cap = common.capacity();
830837
ABSL_ASSUME(cap > 0);
831838
if (cap > Group::kWidth &&
@@ -872,16 +879,10 @@ size_t RehashOrGrowToNextCapacityAndPrepareInsert(
872879
// 762 | 149836 0.37 13 | 148559 0.74 190
873880
// 807 | 149736 0.39 14 | 151107 0.39 14
874881
// 852 | 150204 0.42 15 | 151019 0.42 15
875-
DropDeletesWithoutResize(common, policy);
876-
FindInfo find_info = find_first_non_full(common, hash);
877-
PrepareInsertCommon(common);
878-
common.growth_info().OverwriteEmptyAsFull();
879-
SetCtrl(common, find_info.offset, H2(hash), policy.slot_size);
880-
common.infoz().RecordInsert(hash, find_info.probe_length);
881-
return find_info.offset;
882+
return DropDeletesWithoutResizeAndPrepareInsert(common, new_hash, policy);
882883
} else {
883884
// Otherwise grow the container.
884-
return GrowToNextCapacityAndPrepareInsert(common, hash, policy);
885+
return GrowToNextCapacityAndPrepareInsert(common, new_hash, policy);
885886
}
886887
}
887888

absl/container/internal/raw_hash_set.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,22 @@ inline void SetCtrlInSingleGroupTable(const CommonFields& c, size_t i, h2_t h,
19051905
SetCtrlInSingleGroupTable(c, i, static_cast<ctrl_t>(h), slot_size);
19061906
}
19071907

1908+
// Like SetCtrl, but in a table with capacity >= Group::kWidth - 1,
1909+
// we can save some operations when setting the cloned control byte.
1910+
inline void SetCtrlInLargeTable(const CommonFields& c, size_t i, ctrl_t h,
1911+
size_t slot_size) {
1912+
ABSL_SWISSTABLE_ASSERT(c.capacity() >= Group::kWidth - 1);
1913+
DoSanitizeOnSetCtrl(c, i, h, slot_size);
1914+
ctrl_t* ctrl = c.control();
1915+
ctrl[i] = h;
1916+
ctrl[((i - NumClonedBytes()) & c.capacity()) + NumClonedBytes()] = h;
1917+
}
1918+
// Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`.
1919+
inline void SetCtrlInLargeTable(const CommonFields& c, size_t i, h2_t h,
1920+
size_t slot_size) {
1921+
SetCtrlInLargeTable(c, i, static_cast<ctrl_t>(h), slot_size);
1922+
}
1923+
19081924
// growth_info (which is a size_t) is stored with the backing array.
19091925
constexpr size_t BackingArrayAlignment(size_t align_of_slot) {
19101926
return (std::max)(align_of_slot, alignof(GrowthInfo));

0 commit comments

Comments
 (0)