Skip to content

Commit 7f2185d

Browse files
authored
Merge pull request #34464 from mikeash/concurrentreadablehashmap-move-elementcapacity
[Runtime] Move ElementCapacity into the Elements allocation.
2 parents e27ebf5 + 782fa27 commit 7f2185d

File tree

2 files changed

+41
-22
lines changed

2 files changed

+41
-22
lines changed

include/swift/Runtime/Concurrent.h

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
601601
/// rounds up to the next allocation quantum by calling `malloc_good_size`.
602602
/// Otherwise, just return the passed-in size, which is always valid even if
603603
/// not necessarily optimal.
604-
size_t goodSize(size_t size) {
604+
static size_t goodSize(size_t size) {
605605
#if defined(__APPLE__) && defined(__MACH__)
606606
return malloc_good_size(size);
607607
#else
@@ -693,6 +693,29 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
693693
}
694694
};
695695

696+
/// The struct used for element storage. The `Elem` member is considered to be
697+
/// the first element of a variable-length array, whose size is determined by
698+
/// the allocation.
699+
struct ElementStorage {
700+
uint32_t Capacity;
701+
ElemTy Elem;
702+
703+
static ElementStorage *allocate(size_t capacity) {
704+
auto headerSize = offsetof(ElementStorage, Elem);
705+
auto size = goodSize(headerSize + capacity * sizeof(ElemTy));
706+
707+
auto *ptr = reinterpret_cast<ElementStorage *>(malloc(size));
708+
if (!ptr)
709+
swift::crash("Could not allocate memory.");
710+
711+
ptr->Capacity = (size - headerSize) / sizeof(ElemTy);
712+
713+
return ptr;
714+
}
715+
716+
ElemTy *data() { return &Elem; }
717+
};
718+
696719
/// A simple linked list representing pointers that need to be freed.
697720
struct FreeListNode {
698721
FreeListNode *Next;
@@ -723,17 +746,14 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
723746
std::atomic<uint32_t> ElementCount{0};
724747

725748
/// The array of elements.
726-
std::atomic<ElemTy *> Elements{nullptr};
749+
std::atomic<ElementStorage *> Elements{nullptr};
727750

728751
/// The array of indices.
729752
std::atomic<IndexStorage *> Indices{nullptr};
730753

731754
/// The writer lock, which must be taken before any mutation of the table.
732755
StaticMutex WriterLock;
733756

734-
/// The maximum number of elements that the current elements array can hold.
735-
uint32_t ElementCapacity{0};
736-
737757
/// The list of pointers to be freed once no readers are active.
738758
FreeListNode *FreeList{nullptr};
739759

@@ -754,22 +774,18 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
754774

755775
/// Grow the elements array, adding the old array to the free list and
756776
/// returning the new array with all existing elements copied into it.
757-
ElemTy *resize(ElemTy *elements, size_t elementCount) {
777+
ElementStorage *resize(ElementStorage *elements, size_t elementCount) {
758778
// Grow capacity by 25%, making sure we grow by at least 1.
759779
size_t newCapacity =
760780
std::max(elementCount + (elementCount >> 2), elementCount + 1);
761-
size_t newSize = newCapacity * sizeof(ElemTy);
762-
763-
newSize = goodSize(newSize);
764-
newCapacity = newSize / sizeof(ElemTy);
781+
auto *newElements = ElementStorage::allocate(newCapacity);
765782

766-
ElemTy *newElements = static_cast<ElemTy *>(malloc(newSize));
767783
if (elements) {
768-
memcpy(newElements, elements, elementCount * sizeof(ElemTy));
784+
memcpy(newElements->data(), elements->data(),
785+
elementCount * sizeof(ElemTy));
769786
FreeListNode::add(&FreeList, elements);
770787
}
771788

772-
ElementCapacity = newCapacity;
773789
Elements.store(newElements, std::memory_order_release);
774790
return newElements;
775791
}
@@ -919,16 +935,17 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
919935
// should not happen often.
920936
IndexStorage *indices;
921937
size_t elementCount;
922-
ElemTy *elements;
923-
ElemTy *elements2;
938+
ElementStorage *elements;
939+
ElementStorage *elements2;
924940
do {
925941
elements = Elements.load(std::memory_order_acquire);
926942
indices = Indices.load(std::memory_order_acquire);
927943
elementCount = ElementCount.load(std::memory_order_acquire);
928944
elements2 = Elements.load(std::memory_order_acquire);
929945
} while (elements != elements2);
930946

931-
return Snapshot(this, indices, elements, elementCount);
947+
ElemTy *elementsPtr = elements ? elements->data() : nullptr;
948+
return Snapshot(this, indices, elementsPtr, elementCount);
932949
}
933950

934951
/// Get an element by key, or insert a new element for that key if one is not
@@ -958,8 +975,9 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
958975
auto indicesCapacityLog2 = indices->CapacityLog2;
959976
auto elementCount = ElementCount.load(std::memory_order_relaxed);
960977
auto *elements = Elements.load(std::memory_order_relaxed);
978+
auto *elementsPtr = elements ? elements->data() : nullptr;
961979

962-
auto found = this->find(key, indices, elementCount, elements);
980+
auto found = this->find(key, indices, elementCount, elementsPtr);
963981
if (found.first) {
964982
call(found.first, false);
965983
deallocateFreeListIfSafe();
@@ -973,15 +991,15 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
973991
auto emptyCount = indicesCapacity - (elementCount + 1);
974992
auto proportion = indicesCapacity / emptyCount;
975993
if (proportion >= ResizeProportion) {
976-
indices = resize(indices, indicesCapacityLog2, elements);
977-
found = find(key, indices, elementCount, elements);
994+
indices = resize(indices, indicesCapacityLog2, elementsPtr);
995+
found = find(key, indices, elementCount, elementsPtr);
978996
assert(!found.first && "Shouldn't suddenly find the key after rehashing");
979997
}
980998

981-
if (elementCount >= ElementCapacity) {
999+
if (!elements || elementCount >= elements->Capacity) {
9821000
elements = resize(elements, elementCount);
9831001
}
984-
auto *element = &elements[elementCount];
1002+
auto *element = &elements->data()[elementCount];
9851003

9861004
// Order matters: fill out the element, then update the count,
9871005
// then update the index.
@@ -1010,7 +1028,6 @@ template <class ElemTy> struct ConcurrentReadableHashMap {
10101028
Indices.store(nullptr, std::memory_order_relaxed);
10111029
ElementCount.store(0, std::memory_order_relaxed);
10121030
Elements.store(nullptr, std::memory_order_relaxed);
1013-
ElementCapacity = 0;
10141031

10151032
FreeListNode::add(&FreeList, indices);
10161033
FreeListNode::add(&FreeList, elements);

test/stdlib/symbol-visibility-linux.test-sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA80_cEEEvv \
3232
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA88_cEEEvv \
3333
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA104_cEEEvv \
34+
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA160_cEEEvv \
3435
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA168_cEEEvv \
3536
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA216_cEEEvv \
3637
// RUN: -e _ZN9__gnu_cxx12__to_xstringISscEET_PFiPT0_mPKS2_P13__va_list_tagEmS5_z \
@@ -56,6 +57,7 @@
5657
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA80_cEEEvv \
5758
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA88_cEEEvv \
5859
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA104_cEEEvv \
60+
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA160_cEEEvv \
5961
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA168_cEEEvv \
6062
// RUN: -e _ZSt16__once_call_implISt12_Bind_simpleIFPFvPvEPA216_cEEEvv \
6163
// RUN: -e _ZN9__gnu_cxx12__to_xstringISscEET_PFiPT0_mPKS2_P13__va_list_tagEmS5_z \

0 commit comments

Comments
 (0)