Constant Time std::unordered_map<size_t, size_t> 和个人 hashmap 实现

Constant Time std::unordered_map<size_t, size_t> and personal hashmap implementation

我实现了自己的哈希图,我想将其与 std::unordered_map 进行基准测试。我使用如下函数来测量设置、成功获取和不成功获取所需的纳秒数:

size_t umap_put_speed(std::unordered_map<size_t, size_t> &umap, size_t key, size_t value){
    auto t1 = high_resolution_clock::now();
    umap[key] = value;
    auto t2 = high_resolution_clock::now();
    auto ns_int = duration_cast<nanoseconds>(t2 - t1);
    return ns_int.count();
}

size_t umap_get_speed(std::unordered_map<size_t, size_t> &umap, size_t key){
    auto t1 = high_resolution_clock::now();
    umap.find(key);
    auto t2 = high_resolution_clock::now();
    auto ns_int = duration_cast<nanoseconds>(t2 - t1);
    return ns_int.count();
}

size_t single_put_speed(HashMap<size_t, size_t, GenericHash<size_t> > &hmap, size_t key, size_t value){
    auto t1 = high_resolution_clock::now();
    hmap.put(key, value);
    auto t2 = high_resolution_clock::now();
    auto ns_int = duration_cast<nanoseconds>(t2 - t1);
    return ns_int.count();
}

size_t single_get_speed(HashMap<size_t, size_t, GenericHash<size_t> > &hmap, size_t key){
    size_t value;
    auto t1 = high_resolution_clock::now();
    hmap.get(key, value);
    auto t2 = high_resolution_clock::now();
    auto ns_int = duration_cast<nanoseconds>(t2 - t1);
    return ns_int.count();
}

然后我将时间写入 CSV 文件,如下所示:

void test_put_speed(){
    std::ofstream benchmark_put;
    benchmark_put.open("benchmarks/put.csv");
    
    benchmark_put << "Num Entries,Linear Map,Unordered Map\n";
    
    // Declare hashmaps
    HashMap<size_t, size_t, GenericHash<size_t> > lmap(SMALL_SIZE);
    std::unordered_map<size_t, size_t> umap;
    
    // Put times
    size_t time_lmap_put = 0;
    size_t time_umap_put = 0;
    
    size_t count = 0;
    for(size_t i = 0; i < MAX_ELEM; i++){
        if(count == WINDOW_SIZE){
            benchmark_put << i << ", ";
            benchmark_put << time_lmap_put/(count) << ", ";
            benchmark_put << time_umap_put/(count) << "\n";
    
            time_lmap_put = 0;
            time_umap_put = 0;
    
            count = 0;
        }
    
        time_lmap_put += lmap_put_speed(lmap, i, i);
        time_umap_put += umap_put_speed(umap, i, i);
    
        count += 1;
    }    
}

但是,我得到以下纳秒的插入、成功搜索和不成功搜索:

我知道 hashmap 的设计平均需要 O(1) 时间,但是对于超过一百万的项目,我会认为我添加的项目会从缓存泄漏到主内存中,从而导致访问变慢。我从根本上做错了什么吗?或者我对为什么我期望性能变慢的理解不正确?

虽然这本身不是解决方案,但它是我最终获得一些令我更满意的结果的方式。我改变了两件事。首先,我没有测量单独的插入、查找和删除,而是测量了将 K 个操作链接在一起所花费的时间。这让我更清楚地了解我的实施与 unordered_map 相比有多少 faster/slower。我的新功能如下所示:

double umap_insert_speed(std::unordered_map<size_t, size_t> &umap, std::vector<size_t> keys){
    auto t1 = steady_clock::now();
    for(size_t key : keys){
        umap[key] = 1;    
    }
    auto t2 = steady_clock::now();
    duration<double> s_double = t2 - t1;
    return s_double.count();
}

生成的图表如下所示:

我改变的第二件事是如何在不调用 emplace 函数的情况下绕过我的计时函数。我只是在循环中添加了一个 运行 总和,所以我将使用放置的值:

double umap_emplace_speed(std::unordered_map<size_t, size_t> &umap, std::vector<size_t> keys){
    size_t value;
    size_t sum = 0;
    auto t1 = steady_clock::now();
    for(size_t key: keys){
        umap.emplace(key, value);
        sum += value;
    }
    auto t2 = steady_clock::now();
    duration<double> s_double = t2 - t1;
    return s_double.count();
}