Skip to content

Commit 4b22a29

Browse files
committed
Change MetadataCache to just use malloc/free.
IIRC we never had any evidence that the performance impact of a separate allocator here was actually measurable, and it does come at a significant fragmentation cost because every single cache allocates at least a page of memory. Sharing that with the system allocator makes more sense, even if these allocations are typically permanent. This also means that standard memory-debugging tools will actually find problems with out-of-bounds accesses to metadata.
1 parent 28bbc54 commit 4b22a29

File tree

3 files changed

+21
-137
lines changed

3 files changed

+21
-137
lines changed

stdlib/public/runtime/HeapObject.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
//===----------------------------------------------------------------------===//
1616

1717
#include "swift/Basic/Lazy.h"
18-
#include "swift/Runtime/Concurrent.h"
1918
#include "swift/Runtime/HeapObject.h"
2019
#include "swift/Runtime/Heap.h"
2120
#include "swift/Runtime/Metadata.h"
2221
#include "swift/ABI/System.h"
2322
#include "llvm/Support/MathExtras.h"
23+
#include "MetadataCache.h"
2424
#include "Private.h"
2525
#include "swift/Runtime/Debug.h"
2626
#include <algorithm>
@@ -174,7 +174,7 @@ class BoxCacheEntry {
174174

175175
} // end anonymous namespace
176176

177-
static ConcurrentMap<BoxCacheEntry, false> Boxes;
177+
static SimpleGlobalCache<BoxCacheEntry> Boxes;
178178

179179
SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT
180180
BoxPair::Return

stdlib/public/runtime/Metadata.cpp

Lines changed: 9 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -68,101 +68,6 @@ static int compareIntegers(T left, T right) {
6868
return (left == right ? 0 : left < right ? -1 : 1);
6969
}
7070

71-
static uintptr_t swift_pageSize() {
72-
#if defined(__APPLE__)
73-
return vm_page_size;
74-
#elif defined(_MSC_VER)
75-
SYSTEM_INFO SystemInfo;
76-
GetSystemInfo(&SystemInfo);
77-
return SystemInfo.dwPageSize;
78-
#else
79-
return sysconf(_SC_PAGESIZE);
80-
#endif
81-
}
82-
83-
// allocate memory up to a nearby page boundary
84-
static void *swift_allocateMetadataRoundingToPage(size_t size) {
85-
const uintptr_t PageSizeMask = SWIFT_LAZY_CONSTANT(swift_pageSize()) - 1;
86-
size = (size + PageSizeMask) & ~PageSizeMask;
87-
#if defined(_MSC_VER)
88-
auto mem = VirtualAlloc(
89-
nullptr, size, MEM_TOP_DOWN | MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
90-
#else
91-
auto mem = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE,
92-
VM_TAG_FOR_SWIFT_METADATA, 0);
93-
if (mem == MAP_FAILED)
94-
mem = nullptr;
95-
#endif
96-
return mem;
97-
}
98-
99-
// free memory allocated by swift_allocateMetadataRoundingToPage()
100-
static void swift_freeMetadata(void *addr, size_t size) {
101-
#if defined(_MSC_VER)
102-
// On success, VirtualFree() returns nonzero, on failure 0
103-
int result = VirtualFree(addr, 0, MEM_RELEASE);
104-
if (result == 0)
105-
fatalError(/* flags = */ 0, "swift_freePage: VirtualFree() failed");
106-
#else
107-
// On success, munmap() returns 0, on failure -1
108-
int result = munmap(addr, size);
109-
if (result != 0)
110-
fatalError(/* flags = */ 0, "swift_freePage: munmap() failed");
111-
#endif
112-
}
113-
114-
void *MetadataAllocator::Allocate(size_t size, size_t /*alignment*/) {
115-
const uintptr_t PageSize = SWIFT_LAZY_CONSTANT(swift_pageSize());
116-
// If the requested size is a page or larger, map page(s) for it
117-
// specifically.
118-
if (LLVM_UNLIKELY(size >= PageSize)) {
119-
void *mem = swift_allocateMetadataRoundingToPage(size);
120-
if (!mem)
121-
crash("unable to allocate memory for metadata cache");
122-
return mem;
123-
}
124-
125-
uintptr_t curValue = NextValue.load(std::memory_order_relaxed);
126-
while (true) {
127-
char *next = reinterpret_cast<char*>(curValue);
128-
char *end = next + size;
129-
130-
// If we wrap over the end of the page, allocate a new page.
131-
void *allocation = nullptr;
132-
const uintptr_t PageSizeMask = PageSize - 1;
133-
if (LLVM_UNLIKELY(((uintptr_t)next & ~PageSizeMask)
134-
!= (((uintptr_t)end & ~PageSizeMask)))) {
135-
// Allocate a new page if we haven't already.
136-
allocation = swift_allocateMetadataRoundingToPage(PageSize);
137-
138-
if (!allocation)
139-
crash("unable to allocate memory for metadata cache");
140-
141-
next = (char*) allocation;
142-
end = next + size;
143-
}
144-
145-
// Swap it into place.
146-
if (LLVM_LIKELY(std::atomic_compare_exchange_weak_explicit(
147-
&NextValue, &curValue, reinterpret_cast<uintptr_t>(end),
148-
std::memory_order_relaxed, std::memory_order_relaxed))) {
149-
return next;
150-
}
151-
152-
// If that didn't succeed, and we allocated, free the allocation.
153-
// This potentially causes us to perform multiple mmaps under contention,
154-
// but it keeps the fast path pristine.
155-
if (allocation) {
156-
swift_freeMetadata(allocation, PageSize);
157-
}
158-
}
159-
}
160-
161-
void MetadataAllocator::Deallocate(const void *ptr, size_t size) {
162-
// Borrowed from llvm::BumpPtrAllocator.
163-
__asan_poison_memory_region(ptr, size);
164-
}
165-
16671
namespace {
16772
struct GenericCacheEntry;
16873

@@ -375,7 +280,7 @@ namespace {
375280
}
376281

377282
/// The uniquing structure for ObjC class-wrapper metadata.
378-
static ConcurrentMap<ObjCClassCacheEntry, false> ObjCClassWrappers;
283+
static SimpleGlobalCache<ObjCClassCacheEntry> ObjCClassWrappers;
379284

380285
#endif
381286

@@ -459,7 +364,7 @@ class FunctionCacheEntry {
459364
} // end anonymous namespace
460365

461366
/// The uniquing structure for function type metadata.
462-
static ConcurrentMap<FunctionCacheEntry, false> FunctionTypes;
367+
static SimpleGlobalCache<FunctionCacheEntry> FunctionTypes;
463368

464369
const FunctionTypeMetadata *
465370
swift::swift_getFunctionTypeMetadata1(FunctionTypeFlags flags,
@@ -624,7 +529,7 @@ class TupleCacheEntry {
624529
}
625530

626531
/// The uniquing structure for tuple type metadata.
627-
static ConcurrentMap<TupleCacheEntry, false> TupleTypes;
532+
static SimpleGlobalCache<TupleCacheEntry> TupleTypes;
628533

629534
/// Given a metatype pointer, produce the value-witness table for it.
630535
/// This is equivalent to metatype->ValueWitnesses but more efficient.
@@ -1904,7 +1809,7 @@ namespace {
19041809
}
19051810

19061811
/// The uniquing structure for metatype type metadata.
1907-
static ConcurrentMap<MetatypeCacheEntry, false> MetatypeTypes;
1812+
static SimpleGlobalCache<MetatypeCacheEntry> MetatypeTypes;
19081813

19091814
/// \brief Fetch a uniqued metadata for a metatype type.
19101815
SWIFT_RUNTIME_EXPORT
@@ -1972,11 +1877,11 @@ class ExistentialMetatypeCacheEntry {
19721877
} // end anonymous namespace
19731878

19741879
/// The uniquing structure for existential metatype value witness tables.
1975-
static ConcurrentMap<ExistentialMetatypeValueWitnessTableCacheEntry, false>
1880+
static SimpleGlobalCache<ExistentialMetatypeValueWitnessTableCacheEntry>
19761881
ExistentialMetatypeValueWitnessTables;
19771882

19781883
/// The uniquing structure for existential metatype type metadata.
1979-
static ConcurrentMap<ExistentialMetatypeCacheEntry, false> ExistentialMetatypes;
1884+
static SimpleGlobalCache<ExistentialMetatypeCacheEntry> ExistentialMetatypes;
19801885

19811886
static const ExtraInhabitantsValueWitnessTable
19821887
ExistentialMetatypeValueWitnesses_1 =
@@ -2154,15 +2059,15 @@ class ClassExistentialValueWitnessTableCacheEntry {
21542059
} // end anonymous namespace
21552060

21562061
/// The uniquing structure for existential type metadata.
2157-
static ConcurrentMap<ExistentialCacheEntry, false> ExistentialTypes;
2062+
static SimpleGlobalCache<ExistentialCacheEntry> ExistentialTypes;
21582063

21592064
static const ValueWitnessTable OpaqueExistentialValueWitnesses_0 =
21602065
ValueWitnessTableForBox<OpaqueExistentialBox<0>>::table;
21612066
static const ValueWitnessTable OpaqueExistentialValueWitnesses_1 =
21622067
ValueWitnessTableForBox<OpaqueExistentialBox<1>>::table;
21632068

21642069
/// The uniquing structure for opaque existential value witness tables.
2165-
static ConcurrentMap<OpaqueExistentialValueWitnessTableCacheEntry, false>
2070+
static SimpleGlobalCache<OpaqueExistentialValueWitnessTableCacheEntry>
21662071
OpaqueExistentialValueWitnessTables;
21672072

21682073
/// Instantiate a value witness table for an opaque existential container with
@@ -2208,7 +2113,7 @@ static const ExtraInhabitantsValueWitnessTable ClassExistentialValueWitnesses_2
22082113
ValueWitnessTableForBox<ClassExistentialBox<2>>::table;
22092114

22102115
/// The uniquing structure for class existential value witness tables.
2211-
static ConcurrentMap<ClassExistentialValueWitnessTableCacheEntry, false>
2116+
static SimpleGlobalCache<ClassExistentialValueWitnessTableCacheEntry>
22122117
ClassExistentialValueWitnessTables;
22132118

22142119
/// Instantiate a value witness table for a class-constrained existential

stdlib/public/runtime/MetadataCache.h

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,37 +26,16 @@
2626

2727
namespace swift {
2828

29-
/// A bump pointer for metadata allocations. Since metadata is (currently)
30-
/// never released, it does not support deallocation. This allocator by itself
31-
/// is not thread-safe; in concurrent uses, allocations must be guarded by
32-
/// a lock, such as the per-metadata-cache lock used to guard metadata
33-
/// instantiations. All allocations are pointer-aligned.
34-
class MetadataAllocator : public llvm::AllocatorBase<MetadataAllocator> {
35-
/// Address of the next available space. The allocator grabs a page at a time,
36-
/// so the need for a new page can be determined by page alignment.
37-
///
38-
/// Initializing to -1 instead of nullptr ensures that the first allocation
39-
/// triggers a page allocation since it will always span a "page" boundary.
40-
std::atomic<uintptr_t> NextValue;
41-
42-
public:
43-
constexpr MetadataAllocator() : NextValue(~(uintptr_t)0) {}
44-
45-
// Don't copy or move, please.
46-
MetadataAllocator(const MetadataAllocator &) = delete;
47-
MetadataAllocator(MetadataAllocator &&) = delete;
48-
MetadataAllocator &operator=(const MetadataAllocator &) = delete;
49-
MetadataAllocator &operator=(MetadataAllocator &&) = delete;
50-
51-
void Reset() {}
52-
53-
LLVM_ATTRIBUTE_RETURNS_NONNULL
54-
void *Allocate(size_t size, size_t alignment);
55-
56-
void Deallocate(const void *ptr, size_t size);
57-
58-
void PrintStats() const {}
59-
};
29+
// For now, use malloc and free as our standard allocator for
30+
// metadata caches. It might make sense in the future to take
31+
// advantage of the fact that we know that most allocations here
32+
// won't ever be deallocated.
33+
using MetadataAllocator = llvm::MallocAllocator;
34+
35+
/// A typedef for simple global caches.
36+
template <class EntryTy>
37+
using SimpleGlobalCache =
38+
ConcurrentMap<EntryTy, /*destructor*/ false, MetadataAllocator>;
6039

6140
// A wrapper around a pointer to a metadata cache entry that provides
6241
// DenseMap semantics that compare values in the key vector for the metadata

0 commit comments

Comments
 (0)