常量字符串的高效哈希映射

时间:2016-08-22 09:45:38

标签: c++ c++11

我需要映射常量大小的字符串,其中只包含字母数字值(A-Z,0-9,没有小写字母)到其他字符串。 unordered_map变得非常大(数千万个键),而映射的值来自几千个字符串的集合。在进行分析时,我发现大部分时间都花在将新值插入地图(operator [])中,并且清除地图需要很长时间。

std::unordered_map<std::string, std::string> hashMap;
while (...){
    ...
    hashMap[key] = value;  // ~50% of program time is spent here
    ...
}

hashMap.clear();  // Takes a very long time, at this point hashMap.size() > 20,000,000

我的想法是字符串分配/解除分配非常慢,以及散列和插入地图。 有什么建议可以优化吗?请记住,密钥大小是常量,其内容限制为一组36个字符,并且映射的值来自有限集。我愿意使用除strings和unordered_map之外的不同容器/数据类型。

更新

根据Baum Mit Augen的建议,我将我的密钥类型更改为无符号long long并创建了将base 36转换为decimal的函数:

unsigned long long ConvertBase36(const char* num)
{
    unsigned long long retVal = 0;

    for (int i = 0; i < 12; i++)
    {
        unsigned int digit = 0;
        char currChar = num[i];
        if (currChar <= '9')
        {
            digit = currChar - '0';
        }
        else
        {
            digit = currChar - 'A' + 10;
        }

        retVal *= 36;
        retVal += digit;
    }

    return retVal;
}

这给了我整个程序运行时间大约10%的改进。 然后我再次尝试使用unordered_map reserve函数来查看它是否有任何区别,但它没有。 尝试使用map而不是unordered_map确实差了大约10%,所以我还原了这个改变。 最后用unsigned int替换字符串值会使事情变得更快。

2 个答案:

答案 0 :(得分:4)

两个不相关的建议,但都与std::unordered_map::reserve相关。

首先,由于您的无序地图包含10M的元素,因此在插入时可能会有许多重新分配/重新进行。一开始,您可能希望保留10M的条目。

  

映射的值来自几千个字符串的集合

您应该能够将值本身存储在辅助unordered_set中,并将其reserve置于足够大的位置,以确保insert上没有迭代器失效 - 请参阅{{ 3}}。

您的(主要)unordered_map可以将string映射到std::unordered_set::const_iterator

答案 1 :(得分:0)

对于如此大量的条目,您可能需要考虑使用哈希中的桶数。

对于初学者,您可以使用以下方法查询实现定义的值:

malloc/emalloc

然后你可以玩,看看哪个值产生最好的结果:

unordered_map<T, U> um; cout << um.bucket_count();