簡體   English   中英

為什么 std::unordered_map 很慢,我可以更有效地使用它來緩解這種情況嗎?

[英]Why is std::unordered_map slow, and can I use it more effectively to alleviate that?

我最近發現了一件奇怪的事情。 似乎在完全不緩存的情況下計算 Collat​​z 序列長度比使用std::unordered_map緩存所有元素2 倍以上。

注意我確實從問題中得到了提示gcc std::unordered_map 實現慢嗎? 如果是這樣 - 為什么? 我試圖利用這些知識使std::unordered_map盡可能地發揮作用(我使用了 g++ 4.6,它的性能確實比 g++ 的最新版本更好,並且我試圖指定一個合理的初始桶數,我完全做到了等於地圖必須容納的最大元素數)。

相比之下,使用std::vector緩存一些元素比完全不緩存快 17 倍,比使用std::unordered_map快近 40 倍。

我做錯了什么還是這個容器很慢,為什么? 可以讓它執行得更快嗎? 或者哈希圖本質上是無效的,應該在高性能代碼中盡可能避免?

有問題的基准是:

#include <iostream>
#include <unordered_map>
#include <cstdint>
#include <ctime>

std::uint_fast16_t getCollatzLength(std::uint_fast64_t val) {
    static std::unordered_map <std::uint_fast64_t, std::uint_fast16_t> cache ({{1,1}}, 2168611);

    if(cache.count(val) == 0) {
        if(val%2 == 0)
            cache[val] = getCollatzLength(val/2) + 1;
        else
            cache[val] = getCollatzLength(3*val+1) + 1;
    }

    return cache[val];
}

int main()
{
    std::clock_t tStart = std::clock();

    std::uint_fast16_t largest = 0;
    for(int i = 1; i <= 999999; ++i) {
        auto cmax = getCollatzLength(i);
        if(cmax > largest)
            largest = cmax;
    }
    std::cout << largest << '\n';

    std::cout << "Time taken: " << (double)(std::clock() - tStart)/CLOCKS_PER_SEC << '\n';
}

它輸出: Time taken: 0.761717

而完全沒有緩存的基准測試:

#include <iostream>
#include <unordered_map>
#include <cstdint>
#include <ctime>

std::uint_fast16_t getCollatzLength(std::uint_fast64_t val) {
    std::uint_fast16_t length = 1;
    while(val != 1) {
        if(val%2 == 0)
            val /= 2;
        else
            val = 3*val + 1;
        ++length;
    }
    return length;
}

int main()
{
    std::clock_t tStart = std::clock();

    std::uint_fast16_t largest = 0;
    for(int i = 1; i <= 999999; ++i) {
        auto cmax = getCollatzLength(i);
        if(cmax > largest)
            largest = cmax;
    }
    std::cout << largest << '\n';

    std::cout << "Time taken: " << (double)(std::clock() - tStart)/CLOCKS_PER_SEC << '\n';
}

輸出Time taken: 0.324586

標准庫的映射確實本質上很慢(尤其是std::mapstd::unoredered_map也是如此)。 Google 的 Chandler Carruth 在他的CppCon 2014 演講中解釋了這一點; 簡而言之: std::unordered_map緩存不友好,因為它使用鏈表作為存儲桶。

這個 SO 問題提到了一些有效的哈希映射實現 - 改用其中之一。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM