Skip to content

Commit

Permalink
Updated HopScotchHashTable with bitmap lookup.
Browse files Browse the repository at this point in the history
(I am surprised it worked the first time: all previous tests passed. Of course, I tested the newly added functions before running everything together.)

Signed-off-by: saad0105050 <saad0105050@gmail.com>
  • Loading branch information
saad0105050 committed Sep 21, 2014
1 parent a0ab501 commit a83208d
Showing 1 changed file with 166 additions and 70 deletions.
236 changes: 166 additions & 70 deletions YASI_12/ds.HopScotchHashTable.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include "common.h"
#include "ds.IntLinearProbingHashTable.h"
#include <bitset>

namespace yasi{
namespace ds{
Expand All @@ -11,11 +12,11 @@ struct HopScotchEntry{
typedef HopScotchEntry<Value> self;
size_t key;
Value value;
int bitField; // map of bucket elements
unsigned int bitField; // map of bucket elements
HopScotchEntry() : key(0), bitField(0){}
HopScotchEntry(const size_t& k) : key(k){}
HopScotchEntry(const size_t& k, const Value& v) : key(k), value(v){}
HopScotchEntry(const size_t& k, const Value& v, const int& bf) :
HopScotchEntry(const size_t& k, const Value& v, const unsigned int& bf) :
key(k), value(v), bitField(bf){}
HopScotchEntry(const self& other):key(other.key), value(other.value),bitField(other.bitField){ }
self& operator=(const self& other){
Expand Down Expand Up @@ -72,6 +73,8 @@ class HopScotchHashTable
size_t H; // the neighborhood size
size_t logH; // log of H
size_t _zeroKeyBucket; // the bucket containing (hashed) zero key
const int SCAN_BUCKET_PREV = -1;
const int SCAN_BUCKET_NEXT = 1;
#if _DEBUG
int _numHops;
#endif // _DEBUG
Expand Down Expand Up @@ -126,74 +129,75 @@ class HopScotchHashTable

// swap two bucket entries
void swap(const int bucketOne, const int bucketTwo) const{


// do not swap bitmaps
EntryType temp = table[bucketOne];
table[bucketOne] = table[bucketTwo];
table[bucketTwo] = temp;
}

// push this entry to the right
// returns false if it is not possible to push
// returns true on success
bool pushToRight(const int bucket, int startOfBumping = -1){
if (startOfBumping > 0 ){
// .... bucket startOfBumping ....
if (circularDiff(bucket, startOfBumping) == 1) {
// we traversed the entire array
// this operation is impossible
return false;
}
else{
// continue working below
}
}
else{
// initialize search
startOfBumping = bucket;
}
table[bucketOne].key = table[bucketTwo].key;
table[bucketOne].value = table[bucketTwo].value;

table[bucketTwo].key = temp.key;
table[bucketTwo].value = temp.value;

bool found = false;
int cur = bucket;
}

// try at most H-1 places to the right
int firstBucket = index(table[bucket].key);
// this is the invariant of hopscotch hashing
if (circularDiff(firstBucket, bucket) >= H){
// the table is broken
cerr << "HopScotch invariant broken: item at bucket [" << bucket << "] is originally hashed at bucket [" << firstBucket << "], which is >= H, which is " << H << endl;
return false;
inline void setBitmap(const int bucket, const unsigned int position, const unsigned int bit) const{
if (bucket >= 0 && bucket < _size &&
position >= 0 && position < H &&
bit <= 1){
table[bucket].bitField &= (0x80000000 >> position);
}
}

int last = modSize(firstBucket + H-1);
if (bucket == last){
// this bucket is not allowed to leave the neighborhood
return false;
// returns the next/prev item in bucket
// if no more items, returns -1
// in error, returns -1
inline int jumpInBucket(
const unsigned int bitmap,
const unsigned int curPosInBucket,
const int direction) const{
if (direction != SCAN_BUCKET_NEXT && direction != SCAN_BUCKET_PREV){
cerr << "HopScotchHashTable::nextInBucket(): wrong direction: " << direction << endl;
return -1;
}
int next;
for (; cur != last; cur = next){
next = circularNext(cur);
if (next == startOfBumping) {
// tried all slots
// no way we can bump any more
return false;
}

if ( isNull(next) ){
// insert into blank slot
table[next] = table[bucket];
return true;
}
if (direction == SCAN_BUCKET_NEXT && (curPosInBucket < 0 ||curPosInBucket >= H - 1)){
cerr << "HopScotchHashTable::nextInBucket(): current pos" << curPosInBucket << " is >= (H-1), which is " << (H - 1) << endl;
return -1;
}
// could not place the entry within neighborhood H
// bump the last guy in the neighborhood
// make room
if (pushToRight(last, startOfBumping)){
table[last] = table[bucket];
return true;
if (direction == SCAN_BUCKET_PREV && (curPosInBucket <= 0 || curPosInBucket >= H)){
cerr << "HopScotchHashTable::nextInBucket(): current pos" << curPosInBucket << " is <= 0" << endl;
return -1;
}
else{
// the hopping failed
return false;
int nextPos = curPosInBucket + direction;
unsigned int mask = 0x80000000 >> nextPos;
for (; mask != 0; nextPos += direction){
int nextBit = (bitmap & mask);
if (nextBit) return nextPos;
if (direction == SCAN_BUCKET_NEXT){
mask >>= 1;
}
else{
// scan prev
mask <<= 1;
}
}
// no more items
return -1;
}

// returns the prev item in bucket
// if no more items, returns -1
// in error, returns -1
inline int prevPosInBucket(const unsigned int bitmap, const unsigned int curPosInBucket) const{
return jumpInBucket(bitmap, curPosInBucket, SCAN_BUCKET_PREV);
}

// returns the next item in bucket
// if no more items, returns -1
// in error, returns -1
inline int nextPosInBucket(const unsigned int bitmap, const unsigned int curPosInBucket) const{
return jumpInBucket(bitmap, curPosInBucket, SCAN_BUCKET_NEXT);
}

bool canSwapCurWithEmpty(const int firstBucket, const int cur, const int hashCur, const int emptySlot){
Expand Down Expand Up @@ -272,7 +276,7 @@ class HopScotchHashTable
//when to stop
int remainingSlots = circularDiff(firstBucket, emptySlot);
if (remainingSlots < H){
// the empty slot is wihing the neighborhood of firstBucket
// the empty slot is within the neighborhood of firstBucket
return &table[emptySlot];
}
// H
Expand All @@ -297,6 +301,15 @@ class HopScotchHashTable
{
// candidate for swap
swap(cur, emptySlot);

// calculate bitmap bit positions set/reset
int diff1 = circularDiff(hashCur, cur); // before
int diff1final = circularDiff(hashCur, emptySlot); // after

// update bitmaps
setBitmap(hashCur, diff1, 0);
setBitmap(hashCur, diff1final, 1);

// repeat
return pullFromLeft(firstBucket, cur);
}
Expand All @@ -315,6 +328,7 @@ class HopScotchHashTable
grow();

int firstBucket = index(k);
// try to find an empty slot
int cur = firstBucket;
while (!isNull(cur) ){
cur = circularNext(cur);
Expand All @@ -326,10 +340,13 @@ class HopScotchHashTable
}

// found an empty slot
if (circularDiff(firstBucket, cur) < H){
// empth slot within the neighborhood of firstBucket
int positionInBucket = circularDiff(firstBucket, cur);
if ( positionInBucket < H){
// empty slot within the neighborhood of firstBucket
table[cur].key = k;
_population++;
// set this bit in bitmap of firstBucket
setBitmap(firstBucket, positionInBucket, 1);
return &table[cur];
}
else{
Expand All @@ -338,9 +355,16 @@ class HopScotchHashTable
// pull it within the immediate neighborhood
BucketType* pBucket = pullFromLeft(firstBucket, cur);
if (pBucket){
// now cur contains the final position of emptySlot
// moved the empty slot to the immediate neighborhood
table[cur].key = k;
_population++;

// set this bit in bitmap of firstBucket
int emptyPosFinal = (pBucket - table);
positionInBucket = circularDiff(firstBucket, emptyPosFinal);
setBitmap(firstBucket, positionInBucket, 1);

return &table[cur];
}
else{
Expand All @@ -360,13 +384,14 @@ class HopScotchHashTable
}

int firstBucket = index(k);
const unsigned int bitmap = table[firstBucket].bitField;
#if _DEBUG
// keep track of how many hops till found
_numHops = 0;
#endif
// keep probing to the right
// the item, if exists, must be in the neighborhood H
for (int i = 0; i < H; i = circularNext(i) ){
for (int i = 0; i < H; i = nextPosInBucket(bitmap, i)){
if (table[firstBucket + i].key == k)
return &table[firstBucket + i];
#if _DEBUG
Expand All @@ -385,8 +410,13 @@ class HopScotchHashTable
BucketType* pBucket = lookupKey(k);
if (pBucket){
pBucket->key = 0;
pBucket->bitField = 0;
_population--;

// update bitmap
int firstBucket = index(k);
int cur = (pBucket - table);
int positionInBucket = circularDiff(firstBucket, cur);
setBitmap(firstBucket, positionInBucket, 0);
}
}

Expand Down Expand Up @@ -945,18 +975,84 @@ class HopScotchHashTableTest : public yasi::Test
ASSERT_EQ(32, h2.size());
ASSERT_EQ(h2.H+1, h2.population());
}
void nextPosInBucket(){
IntHashTable h(4, 4);
int cur, bitmap, next;

bitmap = 0xC0000000; cur = -1; next = -1;
ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
<< "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC0000000; cur = h.H; next = -1;
ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
<< "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC0000000; cur = h.H - 1; next = -1;
ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
<< "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC0000000; cur = 1; next = -1;
ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
<< "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC0000000; cur = 0; next = 1;
ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
<< "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC8800000; cur = 4; next = -1; // cur >= H
ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
<< "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC8000000; cur = 1; next = 4;
ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
<< "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC0000001; cur = 1; next = 31;
ASSERT_EQ(next, h.nextPosInBucket(bitmap, cur))
<< "next(" << cur << ") not " << next << " in bitmap " << std::bitset<32>(bitmap);
}

void prevPosInBucket(){
IntHashTable h(4, 4);
int cur, bitmap, prev;

bitmap = 0xC0000000; cur = -1; prev = -1;
ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
<< "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC0000000; cur = 0; prev = -1;
ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
<< "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC0000000; cur = h.H; prev = -1;
ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
<< "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC0000000; cur = 1; prev = 0;
ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
<< "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xA0000000; cur = 2; prev = 0;
ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
<< "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xC8000000; cur = 4; prev = -1; // cur >= H
ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
<< "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);

bitmap = 0xD8000000; cur = 3; prev = 1;
ASSERT_EQ(prev, h.prevPosInBucket(bitmap, cur))
<< "prev(" << cur << ") not " << prev << " in bitmap " << std::bitset<32>(bitmap);
}

};
ADD_TEST_F(HopScotchHashTableTest, circularDiff);
ADD_TEST_F(HopScotchHashTableTest, nextPosInBucket);
ADD_TEST_F(HopScotchHashTableTest, prevPosInBucket);
ADD_TEST_F(HopScotchHashTableTest, canSwapCurWithEmpty);
ADD_TEST_F(HopScotchHashTableTest, pullFromLeft);
ADD_TEST_F(HopScotchHashTableTest, insertRemoveLookup);

//ADD_TEST_F(HopScotchHashTableTest, copyTable);
//ADD_TEST_F(HopScotchHashTableTest, growCondition);
//ADD_TEST_F(HopScotchHashTableTest, shrinkCondition);
//ADD_TEST_F(HopScotchHashTableTest, isKeyZero);
//ADD_TEST_F(HopScotchHashTableTest, remove);

}
}

0 comments on commit a83208d

Please sign in to comment.