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:
Benjamin Kramer
2011-12-27 20:35:07 +00:00
parent 91968489d7
commit c894b02ae0
2 changed files with 60 additions and 61 deletions
+34 -27
View File
@@ -39,11 +39,13 @@ void StringMapImpl::init(unsigned InitSize) {
NumItems = 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
// end.
TheTable[NumBuckets].Item = (StringMapEntryBase*)2;
TheTable[NumBuckets] = (StringMapEntryBase*)2;
}
@@ -60,29 +62,29 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) {
}
unsigned FullHashValue = HashString(Name);
unsigned BucketNo = FullHashValue & (HTSize-1);
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
unsigned ProbeAmt = 1;
int FirstTombstone = -1;
while (1) {
ItemBucket &Bucket = TheTable[BucketNo];
StringMapEntryBase *BucketItem = Bucket.Item;
StringMapEntryBase *BucketItem = TheTable[BucketNo];
// If we found an empty bucket, this key isn't in the table yet, return it.
if (BucketItem == 0) {
// If we found a tombstone, we want to reuse the tombstone instead of an
// empty bucket. This reduces probing.
if (FirstTombstone != -1) {
TheTable[FirstTombstone].FullHashValue = FullHashValue;
HashTable[FirstTombstone] = FullHashValue;
return FirstTombstone;
}
Bucket.FullHashValue = FullHashValue;
HashTable[BucketNo] = FullHashValue;
return BucketNo;
}
if (BucketItem == getTombstoneVal()) {
// Skip over tombstones. However, remember the first one we see.
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
// 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
@@ -115,18 +117,18 @@ int StringMapImpl::FindKey(StringRef Key) const {
if (HTSize == 0) return -1; // Really empty table?
unsigned FullHashValue = HashString(Key);
unsigned BucketNo = FullHashValue & (HTSize-1);
unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1);
unsigned ProbeAmt = 1;
while (1) {
ItemBucket &Bucket = TheTable[BucketNo];
StringMapEntryBase *BucketItem = Bucket.Item;
StringMapEntryBase *BucketItem = TheTable[BucketNo];
// If we found an empty bucket, this key isn't in the table yet, return.
if (BucketItem == 0)
return -1;
if (BucketItem == getTombstoneVal()) {
// 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
// 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
@@ -165,8 +167,8 @@ StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) {
int Bucket = FindKey(Key);
if (Bucket == -1) return 0;
StringMapEntryBase *Result = TheTable[Bucket].Item;
TheTable[Bucket].Item = getTombstoneVal();
StringMapEntryBase *Result = TheTable[Bucket];
TheTable[Bucket] = getTombstoneVal();
--NumItems;
++NumTombstones;
assert(NumItems + NumTombstones <= NumBuckets);
@@ -180,6 +182,7 @@ StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) {
/// the appropriate mod-of-hashtable-size.
void StringMapImpl::RehashTable() {
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
// 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
// iterators to stop at end.
ItemBucket *NewTableArray =(ItemBucket*)calloc(NewSize+1, sizeof(ItemBucket));
NewTableArray[NewSize].Item = (StringMapEntryBase*)2;
StringMapEntryBase **NewTableArray =
(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
// the hash values available, so we don't have to rehash any strings.
for (ItemBucket *IB = TheTable, *E = TheTable+NumBuckets; IB != E; ++IB) {
if (IB->Item && IB->Item != getTombstoneVal()) {
for (unsigned I = 0, E = NumBuckets; I != E; ++I) {
StringMapEntryBase *Bucket = TheTable[I];
if (Bucket && Bucket != getTombstoneVal()) {
// Fast case, bucket available.
unsigned FullHash = IB->FullHashValue;
unsigned FullHash = HashTable[I];
unsigned NewBucket = FullHash & (NewSize-1);
if (NewTableArray[NewBucket].Item == 0) {
NewTableArray[FullHash & (NewSize-1)].Item = IB->Item;
NewTableArray[FullHash & (NewSize-1)].FullHashValue = FullHash;
if (NewTableArray[NewBucket] == 0) {
NewTableArray[FullHash & (NewSize-1)] = Bucket;
NewHashArray[FullHash & (NewSize-1)] = FullHash;
continue;
}
@@ -214,11 +221,11 @@ void StringMapImpl::RehashTable() {
unsigned ProbeSize = 1;
do {
NewBucket = (NewBucket + ProbeSize++) & (NewSize-1);
} while (NewTableArray[NewBucket].Item);
} while (NewTableArray[NewBucket]);
// Finally found a slot. Fill it in.
NewTableArray[NewBucket].Item = IB->Item;
NewTableArray[NewBucket].FullHashValue = FullHash;
NewTableArray[NewBucket] = Bucket;
NewHashArray[NewBucket] = FullHash;
}
}