mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-11-01 00:17:01 +00:00
Switch StringMap from an array of structures to a structure of arrays.
- -25% memory usage of the main table on x86_64 (was wasted in struct padding). - no significant performance change. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@147294 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -51,20 +51,11 @@ public:
|
|||||||
/// StringMapImpl - This is the base class of StringMap that is shared among
|
/// StringMapImpl - This is the base class of StringMap that is shared among
|
||||||
/// all of its instantiations.
|
/// all of its instantiations.
|
||||||
class StringMapImpl {
|
class StringMapImpl {
|
||||||
public:
|
|
||||||
/// ItemBucket - The hash table consists of an array of these. If Item is
|
|
||||||
/// non-null, this is an extant entry, otherwise, it is a hole.
|
|
||||||
struct ItemBucket {
|
|
||||||
/// FullHashValue - This remembers the full hash value of the key for
|
|
||||||
/// easy scanning.
|
|
||||||
unsigned FullHashValue;
|
|
||||||
|
|
||||||
/// Item - This is a pointer to the actual item object.
|
|
||||||
StringMapEntryBase *Item;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ItemBucket *TheTable;
|
// Array of NumBuckets pointers to entries, null pointers are holes.
|
||||||
|
// TheTable[NumBuckets] contains a sentinel value for easy iteration. Follwed
|
||||||
|
// by an array of the actual hash values as unsigned integers.
|
||||||
|
StringMapEntryBase **TheTable;
|
||||||
unsigned NumBuckets;
|
unsigned NumBuckets;
|
||||||
unsigned NumItems;
|
unsigned NumItems;
|
||||||
unsigned NumTombstones;
|
unsigned NumTombstones;
|
||||||
@@ -320,13 +311,13 @@ public:
|
|||||||
/// insert it and return true.
|
/// insert it and return true.
|
||||||
bool insert(MapEntryTy *KeyValue) {
|
bool insert(MapEntryTy *KeyValue) {
|
||||||
unsigned BucketNo = LookupBucketFor(KeyValue->getKey());
|
unsigned BucketNo = LookupBucketFor(KeyValue->getKey());
|
||||||
ItemBucket &Bucket = TheTable[BucketNo];
|
StringMapEntryBase *&Bucket = TheTable[BucketNo];
|
||||||
if (Bucket.Item && Bucket.Item != getTombstoneVal())
|
if (Bucket && Bucket != getTombstoneVal())
|
||||||
return false; // Already exists in map.
|
return false; // Already exists in map.
|
||||||
|
|
||||||
if (Bucket.Item == getTombstoneVal())
|
if (Bucket == getTombstoneVal())
|
||||||
--NumTombstones;
|
--NumTombstones;
|
||||||
Bucket.Item = KeyValue;
|
Bucket = KeyValue;
|
||||||
++NumItems;
|
++NumItems;
|
||||||
assert(NumItems + NumTombstones <= NumBuckets);
|
assert(NumItems + NumTombstones <= NumBuckets);
|
||||||
|
|
||||||
@@ -340,10 +331,11 @@ public:
|
|||||||
|
|
||||||
// Zap all values, resetting the keys back to non-present (not tombstone),
|
// Zap all values, resetting the keys back to non-present (not tombstone),
|
||||||
// which is safe because we're removing all elements.
|
// which is safe because we're removing all elements.
|
||||||
for (ItemBucket *I = TheTable, *E = TheTable+NumBuckets; I != E; ++I) {
|
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
|
||||||
if (I->Item && I->Item != getTombstoneVal()) {
|
StringMapEntryBase *&Bucket = TheTable[I];
|
||||||
static_cast<MapEntryTy*>(I->Item)->Destroy(Allocator);
|
if (Bucket && Bucket != getTombstoneVal()) {
|
||||||
I->Item = 0;
|
static_cast<MapEntryTy*>(Bucket)->Destroy(Allocator);
|
||||||
|
Bucket = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,21 +349,21 @@ public:
|
|||||||
template <typename InitTy>
|
template <typename InitTy>
|
||||||
MapEntryTy &GetOrCreateValue(StringRef Key, InitTy Val) {
|
MapEntryTy &GetOrCreateValue(StringRef Key, InitTy Val) {
|
||||||
unsigned BucketNo = LookupBucketFor(Key);
|
unsigned BucketNo = LookupBucketFor(Key);
|
||||||
ItemBucket &Bucket = TheTable[BucketNo];
|
StringMapEntryBase *&Bucket = TheTable[BucketNo];
|
||||||
if (Bucket.Item && Bucket.Item != getTombstoneVal())
|
if (Bucket && Bucket != getTombstoneVal())
|
||||||
return *static_cast<MapEntryTy*>(Bucket.Item);
|
return *static_cast<MapEntryTy*>(Bucket);
|
||||||
|
|
||||||
MapEntryTy *NewItem =
|
MapEntryTy *NewItem =
|
||||||
MapEntryTy::Create(Key.begin(), Key.end(), Allocator, Val);
|
MapEntryTy::Create(Key.begin(), Key.end(), Allocator, Val);
|
||||||
|
|
||||||
if (Bucket.Item == getTombstoneVal())
|
if (Bucket == getTombstoneVal())
|
||||||
--NumTombstones;
|
--NumTombstones;
|
||||||
++NumItems;
|
++NumItems;
|
||||||
assert(NumItems + NumTombstones <= NumBuckets);
|
assert(NumItems + NumTombstones <= NumBuckets);
|
||||||
|
|
||||||
// Fill in the bucket for the hash table. The FullHashValue was already
|
// Fill in the bucket for the hash table. The FullHashValue was already
|
||||||
// filled in by LookupBucketFor.
|
// filled in by LookupBucketFor.
|
||||||
Bucket.Item = NewItem;
|
Bucket = NewItem;
|
||||||
|
|
||||||
RehashTable();
|
RehashTable();
|
||||||
return *NewItem;
|
return *NewItem;
|
||||||
@@ -410,21 +402,21 @@ public:
|
|||||||
template<typename ValueTy>
|
template<typename ValueTy>
|
||||||
class StringMapConstIterator {
|
class StringMapConstIterator {
|
||||||
protected:
|
protected:
|
||||||
StringMapImpl::ItemBucket *Ptr;
|
StringMapEntryBase **Ptr;
|
||||||
public:
|
public:
|
||||||
typedef StringMapEntry<ValueTy> value_type;
|
typedef StringMapEntry<ValueTy> value_type;
|
||||||
|
|
||||||
explicit StringMapConstIterator(StringMapImpl::ItemBucket *Bucket,
|
explicit StringMapConstIterator(StringMapEntryBase **Bucket,
|
||||||
bool NoAdvance = false)
|
bool NoAdvance = false)
|
||||||
: Ptr(Bucket) {
|
: Ptr(Bucket) {
|
||||||
if (!NoAdvance) AdvancePastEmptyBuckets();
|
if (!NoAdvance) AdvancePastEmptyBuckets();
|
||||||
}
|
}
|
||||||
|
|
||||||
const value_type &operator*() const {
|
const value_type &operator*() const {
|
||||||
return *static_cast<StringMapEntry<ValueTy>*>(Ptr->Item);
|
return *static_cast<StringMapEntry<ValueTy>*>(*Ptr);
|
||||||
}
|
}
|
||||||
const value_type *operator->() const {
|
const value_type *operator->() const {
|
||||||
return static_cast<StringMapEntry<ValueTy>*>(Ptr->Item);
|
return static_cast<StringMapEntry<ValueTy>*>(*Ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const StringMapConstIterator &RHS) const {
|
bool operator==(const StringMapConstIterator &RHS) const {
|
||||||
@@ -445,7 +437,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void AdvancePastEmptyBuckets() {
|
void AdvancePastEmptyBuckets() {
|
||||||
while (Ptr->Item == 0 || Ptr->Item == StringMapImpl::getTombstoneVal())
|
while (*Ptr == 0 || *Ptr == StringMapImpl::getTombstoneVal())
|
||||||
++Ptr;
|
++Ptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -453,15 +445,15 @@ private:
|
|||||||
template<typename ValueTy>
|
template<typename ValueTy>
|
||||||
class StringMapIterator : public StringMapConstIterator<ValueTy> {
|
class StringMapIterator : public StringMapConstIterator<ValueTy> {
|
||||||
public:
|
public:
|
||||||
explicit StringMapIterator(StringMapImpl::ItemBucket *Bucket,
|
explicit StringMapIterator(StringMapEntryBase **Bucket,
|
||||||
bool NoAdvance = false)
|
bool NoAdvance = false)
|
||||||
: StringMapConstIterator<ValueTy>(Bucket, NoAdvance) {
|
: StringMapConstIterator<ValueTy>(Bucket, NoAdvance) {
|
||||||
}
|
}
|
||||||
StringMapEntry<ValueTy> &operator*() const {
|
StringMapEntry<ValueTy> &operator*() const {
|
||||||
return *static_cast<StringMapEntry<ValueTy>*>(this->Ptr->Item);
|
return *static_cast<StringMapEntry<ValueTy>*>(*this->Ptr);
|
||||||
}
|
}
|
||||||
StringMapEntry<ValueTy> *operator->() const {
|
StringMapEntry<ValueTy> *operator->() const {
|
||||||
return static_cast<StringMapEntry<ValueTy>*>(this->Ptr->Item);
|
return static_cast<StringMapEntry<ValueTy>*>(*this->Ptr);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -39,11 +39,13 @@ void StringMapImpl::init(unsigned InitSize) {
|
|||||||
NumItems = 0;
|
NumItems = 0;
|
||||||
NumTombstones = 0;
|
NumTombstones = 0;
|
||||||
|
|
||||||
TheTable = (ItemBucket*)calloc(NumBuckets+1, sizeof(ItemBucket));
|
TheTable = (StringMapEntryBase **)calloc(NumBuckets+1,
|
||||||
|
sizeof(StringMapEntryBase **) +
|
||||||
|
sizeof(unsigned));
|
||||||
|
|
||||||
// Allocate one extra bucket, set it to look filled so the iterators stop at
|
// Allocate one extra bucket, set it to look filled so the iterators stop at
|
||||||
// end.
|
// end.
|
||||||
TheTable[NumBuckets].Item = (StringMapEntryBase*)2;
|
TheTable[NumBuckets] = (StringMapEntryBase*)2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -60,29 +62,29 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
|
|||||||
}
|
}
|
||||||
unsigned FullHashValue = HashString(Name);
|
unsigned FullHashValue = HashString(Name);
|
||||||
unsigned BucketNo = FullHashValue & (HTSize-1);
|
unsigned BucketNo = FullHashValue & (HTSize-1);
|
||||||
|
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
|
||||||
|
|
||||||
unsigned ProbeAmt = 1;
|
unsigned ProbeAmt = 1;
|
||||||
int FirstTombstone = -1;
|
int FirstTombstone = -1;
|
||||||
while (1) {
|
while (1) {
|
||||||
ItemBucket &Bucket = TheTable[BucketNo];
|
StringMapEntryBase *BucketItem = TheTable[BucketNo];
|
||||||
StringMapEntryBase *BucketItem = Bucket.Item;
|
|
||||||
// If we found an empty bucket, this key isn't in the table yet, return it.
|
// If we found an empty bucket, this key isn't in the table yet, return it.
|
||||||
if (BucketItem == 0) {
|
if (BucketItem == 0) {
|
||||||
// If we found a tombstone, we want to reuse the tombstone instead of an
|
// If we found a tombstone, we want to reuse the tombstone instead of an
|
||||||
// empty bucket. This reduces probing.
|
// empty bucket. This reduces probing.
|
||||||
if (FirstTombstone != -1) {
|
if (FirstTombstone != -1) {
|
||||||
TheTable[FirstTombstone].FullHashValue = FullHashValue;
|
HashTable[FirstTombstone] = FullHashValue;
|
||||||
return FirstTombstone;
|
return FirstTombstone;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bucket.FullHashValue = FullHashValue;
|
HashTable[BucketNo] = FullHashValue;
|
||||||
return BucketNo;
|
return BucketNo;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BucketItem == getTombstoneVal()) {
|
if (BucketItem == getTombstoneVal()) {
|
||||||
// Skip over tombstones. However, remember the first one we see.
|
// Skip over tombstones. However, remember the first one we see.
|
||||||
if (FirstTombstone == -1) FirstTombstone = BucketNo;
|
if (FirstTombstone == -1) FirstTombstone = BucketNo;
|
||||||
} else if (Bucket.FullHashValue == FullHashValue) {
|
} else if (HashTable[BucketNo] == FullHashValue) {
|
||||||
// If the full hash value matches, check deeply for a match. The common
|
// If the full hash value matches, check deeply for a match. The common
|
||||||
// case here is that we are only looking at the buckets (for item info
|
// case here is that we are only looking at the buckets (for item info
|
||||||
// being non-null and for the full hash value) not at the items. This
|
// being non-null and for the full hash value) not at the items. This
|
||||||
@@ -115,18 +117,18 @@ int StringMapImpl::FindKey(StringRef Key) const {
|
|||||||
if (HTSize == 0) return -1; // Really empty table?
|
if (HTSize == 0) return -1; // Really empty table?
|
||||||
unsigned FullHashValue = HashString(Key);
|
unsigned FullHashValue = HashString(Key);
|
||||||
unsigned BucketNo = FullHashValue & (HTSize-1);
|
unsigned BucketNo = FullHashValue & (HTSize-1);
|
||||||
|
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
|
||||||
|
|
||||||
unsigned ProbeAmt = 1;
|
unsigned ProbeAmt = 1;
|
||||||
while (1) {
|
while (1) {
|
||||||
ItemBucket &Bucket = TheTable[BucketNo];
|
StringMapEntryBase *BucketItem = TheTable[BucketNo];
|
||||||
StringMapEntryBase *BucketItem = Bucket.Item;
|
|
||||||
// If we found an empty bucket, this key isn't in the table yet, return.
|
// If we found an empty bucket, this key isn't in the table yet, return.
|
||||||
if (BucketItem == 0)
|
if (BucketItem == 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (BucketItem == getTombstoneVal()) {
|
if (BucketItem == getTombstoneVal()) {
|
||||||
// Ignore tombstones.
|
// Ignore tombstones.
|
||||||
} else if (Bucket.FullHashValue == FullHashValue) {
|
} else if (HashTable[BucketNo] == FullHashValue) {
|
||||||
// If the full hash value matches, check deeply for a match. The common
|
// If the full hash value matches, check deeply for a match. The common
|
||||||
// case here is that we are only looking at the buckets (for item info
|
// case here is that we are only looking at the buckets (for item info
|
||||||
// being non-null and for the full hash value) not at the items. This
|
// being non-null and for the full hash value) not at the items. This
|
||||||
@@ -165,8 +167,8 @@ StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) {
|
|||||||
int Bucket = FindKey(Key);
|
int Bucket = FindKey(Key);
|
||||||
if (Bucket == -1) return 0;
|
if (Bucket == -1) return 0;
|
||||||
|
|
||||||
StringMapEntryBase *Result = TheTable[Bucket].Item;
|
StringMapEntryBase *Result = TheTable[Bucket];
|
||||||
TheTable[Bucket].Item = getTombstoneVal();
|
TheTable[Bucket] = getTombstoneVal();
|
||||||
--NumItems;
|
--NumItems;
|
||||||
++NumTombstones;
|
++NumTombstones;
|
||||||
assert(NumItems + NumTombstones <= NumBuckets);
|
assert(NumItems + NumTombstones <= NumBuckets);
|
||||||
@@ -180,6 +182,7 @@ StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) {
|
|||||||
/// the appropriate mod-of-hashtable-size.
|
/// the appropriate mod-of-hashtable-size.
|
||||||
void StringMapImpl::RehashTable() {
|
void StringMapImpl::RehashTable() {
|
||||||
unsigned NewSize;
|
unsigned NewSize;
|
||||||
|
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
|
||||||
|
|
||||||
// If the hash table is now more than 3/4 full, or if fewer than 1/8 of
|
// If the hash table is now more than 3/4 full, or if fewer than 1/8 of
|
||||||
// the buckets are empty (meaning that many are filled with tombstones),
|
// the buckets are empty (meaning that many are filled with tombstones),
|
||||||
@@ -194,19 +197,23 @@ void StringMapImpl::RehashTable() {
|
|||||||
|
|
||||||
// Allocate one extra bucket which will always be non-empty. This allows the
|
// Allocate one extra bucket which will always be non-empty. This allows the
|
||||||
// iterators to stop at end.
|
// iterators to stop at end.
|
||||||
ItemBucket *NewTableArray =(ItemBucket*)calloc(NewSize+1, sizeof(ItemBucket));
|
StringMapEntryBase **NewTableArray =
|
||||||
NewTableArray[NewSize].Item = (StringMapEntryBase*)2;
|
(StringMapEntryBase **)calloc(NewSize+1, sizeof(StringMapEntryBase *) +
|
||||||
|
sizeof(unsigned));
|
||||||
|
unsigned *NewHashArray = (unsigned *)(NewTableArray + NewSize + 1);
|
||||||
|
NewTableArray[NewSize] = (StringMapEntryBase*)2;
|
||||||
|
|
||||||
// Rehash all the items into their new buckets. Luckily :) we already have
|
// Rehash all the items into their new buckets. Luckily :) we already have
|
||||||
// the hash values available, so we don't have to rehash any strings.
|
// the hash values available, so we don't have to rehash any strings.
|
||||||
for (ItemBucket *IB = TheTable, *E = TheTable+NumBuckets; IB != E; ++IB) {
|
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
|
||||||
if (IB->Item && IB->Item != getTombstoneVal()) {
|
StringMapEntryBase *Bucket = TheTable[I];
|
||||||
|
if (Bucket && Bucket != getTombstoneVal()) {
|
||||||
// Fast case, bucket available.
|
// Fast case, bucket available.
|
||||||
unsigned FullHash = IB->FullHashValue;
|
unsigned FullHash = HashTable[I];
|
||||||
unsigned NewBucket = FullHash & (NewSize-1);
|
unsigned NewBucket = FullHash & (NewSize-1);
|
||||||
if (NewTableArray[NewBucket].Item == 0) {
|
if (NewTableArray[NewBucket] == 0) {
|
||||||
NewTableArray[FullHash & (NewSize-1)].Item = IB->Item;
|
NewTableArray[FullHash & (NewSize-1)] = Bucket;
|
||||||
NewTableArray[FullHash & (NewSize-1)].FullHashValue = FullHash;
|
NewHashArray[FullHash & (NewSize-1)] = FullHash;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,11 +221,11 @@ void StringMapImpl::RehashTable() {
|
|||||||
unsigned ProbeSize = 1;
|
unsigned ProbeSize = 1;
|
||||||
do {
|
do {
|
||||||
NewBucket = (NewBucket + ProbeSize++) & (NewSize-1);
|
NewBucket = (NewBucket + ProbeSize++) & (NewSize-1);
|
||||||
} while (NewTableArray[NewBucket].Item);
|
} while (NewTableArray[NewBucket]);
|
||||||
|
|
||||||
// Finally found a slot. Fill it in.
|
// Finally found a slot. Fill it in.
|
||||||
NewTableArray[NewBucket].Item = IB->Item;
|
NewTableArray[NewBucket] = Bucket;
|
||||||
NewTableArray[NewBucket].FullHashValue = FullHash;
|
NewHashArray[NewBucket] = FullHash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user