Skip to content

Commit 3c73685

Browse files
committed
runtime: avoid memory allocations in checkTransitiveCompleteness
Use SmallVector instead of std::vector and a SmallPtrSet-like implementation for the set.
1 parent 0528d8a commit 3c73685

File tree

1 file changed

+27
-7
lines changed

1 file changed

+27
-7
lines changed

stdlib/public/runtime/Metadata.cpp

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4740,20 +4740,38 @@ areAllTransitiveMetadataComplete_cheap(const Metadata *type) {
47404740
/// dependencies actually hold, and we can keep going.
47414741
static MetadataDependency
47424742
checkTransitiveCompleteness(const Metadata *initialType) {
4743-
// TODO: it would nice to avoid allocating memory in common cases here.
4744-
// In particular, we don't usually add *anything* to the worklist, and we
4745-
// usually only add a handful of types to the map.
4746-
std::vector<const Metadata *> worklist;
4747-
std::unordered_set<const Metadata *> presumedCompleteTypes;
4743+
llvm::SmallVector<const Metadata *, 8> worklist;
4744+
4745+
// An efficient hash-set implementation in the spirit of llvm's SmallPtrSet:
4746+
// The first 8 elements are stored in an inline-allocated array to avoid
4747+
// malloc calls in the common case. Lookup is still reasonable fast because
4748+
// there are max 8 elements in the array.
4749+
const int InlineCapacity = 8;
4750+
const Metadata *inlinedPresumedCompleteTypes[InlineCapacity];
4751+
int numInlinedTypes = 0;
4752+
std::unordered_set<const Metadata *> overflowPresumedCompleteTypes;
47484753

47494754
MetadataDependency dependency;
47504755
auto isIncomplete = [&](const Metadata *type) -> bool {
47514756
// Add the type to the presumed-complete-types set. If this doesn't
47524757
// succeed, we've already inserted it, which means we must have already
47534758
// decided it was complete.
4754-
if (!presumedCompleteTypes.insert(type).second)
4759+
// First, try to find the type in the inline-storage of the set.
4760+
const Metadata **end = inlinedPresumedCompleteTypes + numInlinedTypes;
4761+
if (std::find(inlinedPresumedCompleteTypes, end, type) != end)
47554762
return false;
47564763

4764+
// We didn't find the type in the inline-storage.
4765+
if (numInlinedTypes < InlineCapacity) {
4766+
assert(overflowPresumedCompleteTypes.size() == 0);
4767+
inlinedPresumedCompleteTypes[numInlinedTypes++] = type;
4768+
} else {
4769+
// The inline-storage is full. So try to insert the type into the
4770+
// overflow set.
4771+
if (!overflowPresumedCompleteTypes.insert(type).second)
4772+
return false;
4773+
}
4774+
47574775
// Check the metadata's current state with a non-blocking request.
47584776
auto request = MetadataRequest(MetadataState::Complete,
47594777
/*non-blocking*/ true);
@@ -4780,7 +4798,9 @@ checkTransitiveCompleteness(const Metadata *initialType) {
47804798

47814799
// Consider the type itself to be presumed-complete. We're looking for
47824800
// a greatest fixed point.
4783-
presumedCompleteTypes.insert(initialType);
4801+
assert(numInlinedTypes == 0 && overflowPresumedCompleteTypes.size() == 0);
4802+
inlinedPresumedCompleteTypes[0] = initialType;
4803+
numInlinedTypes = 1;
47844804
if (findAnyTransitiveMetadata(initialType, isIncomplete))
47854805
return dependency;
47864806

0 commit comments

Comments
 (0)