Task #1 - Extendible Hash Table
- 一、题目链接
- 二、准备工作
- 三、部分实现
- 四、自定义测试用例
一、题目链接
二、准备工作
见 CMU 15-445 Project #0 - C++ Primer 中的准备工作。
三、部分实现
Find
auto Find(const K &key, V &value) -> bool override {
std::scoped_lock<std::mutex> locker(latch_);
return dir_.at(IndexOf(key))->Find(key, value);
}
Insert
void Insert(const K &key, const V &value)
override {
std::scoped_lock <std::mutex> locker(latch_);
DoInsert(key, value
); // 通过DoInsert将整个插入操作整合为一个原子操作
}
void DoInsert(const K &key, const V &value) {
/* 如果插入成功,直接返回。 */
if (dir_.at(IndexOf(key))->Insert(key, value)) {
return;
}
/* 获取原始桶的目录下标和分裂桶的目录下标 */
int mask = (1 << (dir_.at(IndexOf(key))->GetDepth() + 1)) - 1;
size_t primary_dir_index = IndexOf(key) & mask;
size_t divide_dir_index = primary_dir_index ^ (1 << dir_.at(IndexOf(key))->GetDepth());
/* 如果发生桶溢出,需要根据局部深度和全局深度的关系进行相应处理:
* 如果局部深度等于全局深度,需要进行目录扩展、桶分裂和重映射;
* 如果局部深度小于全局深度,只需要进行桶分裂和重映射。 */
/* 目录扩展 */
if (dir_.at(primary_dir_index)->GetDepth() == global_depth_) {
int primary_dir_len = dir_.size(); // 扩展前的目录长度
/* 增加全局深度 */
global_depth_++;
/* 新扩展的shared_ptr依次指向原来的桶 */
for (int i = 0; i < primary_dir_len; i++) {
dir_.emplace_back(std::shared_ptr<Bucket>(dir_.at(i)));
}
}
/* 增加原始桶的局部深度 */
dir_.at(primary_dir_index)->IncrementDepth();
/* 桶分裂 */
dir_.at(divide_dir_index) = std::make_shared<Bucket>(bucket_size_, dir_.at(primary_dir_index)->GetDepth());
num_buckets_++;
/* 重新映射原始桶中的pair */
auto primary_items = dir_.at(primary_dir_index)->GetItems(); // 单独提取以避免迭代器失效
for (const auto &[k, v] : primary_items) {
/* 如果当前pair的key值不再映射到原始桶,需要将对应pair从原始桶中删除后插入到新桶中。 */
if ((IndexOf(k) & mask) == divide_dir_index) {
dir_.at(primary_dir_index)->Remove(k);
dir_.at(divide_dir_index)->Insert(k, v);
}
}
/* 经过重新映射后,如果目标桶未满直接插入即可,否则需要递归插入并再次进行桶分裂。*/
if (!dir_.at(IndexOf(key))->IsFull()) {
dir_.at(IndexOf(key))->Insert(key, value);
return;
}
DoInsert(key, value);
}
Remove
auto Remove(const K &key) -> bool override {
std::scoped_lock<std::mutex> locker(latch_);
/* 如果查找失败直接返回 */
V find_value;
if (!dir_.at(IndexOf(key))->Find(key, find_value)) {
return false;
}
/* 删除 */
dir_.at(IndexOf(key))->Remove(key);
return true;
}
提交后的结果如下,其中可扩展哈希表部分只有一个 ExtendibleHashTableTest.ConcurrentInsertFind.ASAN
的测试用例没过,其余全部通过。这里推测可能是多线程加锁时存在死锁,暂时还没有解决,不过整体思路应该没有问题。
四、自定义测试用例
TEST(ExtendibleHashTableTest, GetNumBucketsTest) {
auto table = std::make_unique<ExtendibleHashTable<int, std::string>>(2);
table->Insert(0b0000100, "01"); // 04
table->Insert(0b0001100, "02"); // 12
table->Insert(0b0010000, "03"); // 16
EXPECT_EQ(4, table->GetNumBuckets());
table->Insert(0b1000000, "04"); // 64
table->Insert(0b0011111, "05"); // 31
table->Insert(0b0001010, "06"); // 10
table->Insert(0b0110011, "07"); // 51
EXPECT_EQ(4, table->GetNumBuckets());
table->Insert(0b0001111, "08"); // 15
table->Insert(0b0010010, "09"); // 18
table->Insert(0b0010100, "10"); // 20
EXPECT_EQ(7, table->GetNumBuckets());
table->Insert(0b0000111, "11"); // 07
table->Insert(0b0010111, "12"); // 23
EXPECT_EQ(8, table->GetNumBuckets());
}
参考 :
https://zhuanlan.zhihu.com/p/622221722
https://www.geeksforgeeks.org/extendible-hashing-dynamic-approach-to-dbms/