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/




















