C++ 长字符串密钥的快速哈希函数

C++ 长字符串密钥的快速哈希函数,c++,hash-function,C++,Hash Function,我使用的是一个可扩展散列,我想把字符串作为键。问题是,我正在使用的当前哈希函数会迭代整个字符串/键,我认为这对程序的性能非常不利,因为哈希函数会被多次调用,尤其是在拆分存储桶时 当前哈希函数 int hash(const string& key) { int seed = 131; unsigned long hash = 0; for(unsigned i = 0; i < key.length(); i++) { hash = (

我使用的是一个可扩展散列,我想把字符串作为键。问题是,我正在使用的当前哈希函数会迭代整个字符串/键,我认为这对程序的性能非常不利,因为哈希函数会被多次调用,尤其是在拆分存储桶时

当前哈希函数

int hash(const string& key)
{
    int seed = 131;
    unsigned long hash = 0;
    for(unsigned i = 0; i < key.length(); i++)
    {
        hash = (hash * seed) + key[i];
    }
    return hash;
}

我在互联网上搜索了一个更好的,但我没有找到任何符合我的情况。有什么建议吗?

您应该更喜欢使用
std::hash
,除非测量结果表明您可以做得更好。要限制其使用的字符数,请使用以下命令:

    const auto limit = min(key.length(), 16);
    for(unsigned i = 0; i < limit; i++)
const auto limit=min(key.length(),16);
for(无符号i=0;i
您需要进行实验,以找到16的最佳值

实际上,我希望性能会变得更差(因为会有更多的碰撞)。如果您的字符串是几个k,那么限制在前64个字节可能是值得的


根据您的字符串,它可能值得从一开始就开始,而不是从一开始。例如,对文件名进行哈希处理时,如果使用结尾处20到5之间的字符(忽略通常为常量的路径名前缀和文件扩展名),您可能会做得更好。但是你仍然需要测量。

你应该更喜欢使用
std::hash
,除非测量表明你可以做得更好。要限制其使用的字符数,请使用以下命令:

    const auto limit = min(key.length(), 16);
    for(unsigned i = 0; i < limit; i++)
const auto limit=min(key.length(),16);
for(无符号i=0;i
您需要进行实验,以找到16的最佳值

实际上,我希望性能会变得更差(因为会有更多的碰撞)。如果您的字符串是几个k,那么限制在前64个字节可能是值得的


根据您的字符串,它可能值得从一开始就开始,而不是从一开始。例如,对文件名进行哈希处理时,如果使用结尾处20到5之间的字符(忽略通常为常量的路径名前缀和文件扩展名),您可能会做得更好。但是您仍然需要测量。

您可以直接使用
std::hash
而不是实现自己的函数

#include <iostream>
#include <functional>
#include <string>

size_t hash(const std::string& key)
{
    std::hash<std::string> hasher;
    return hasher(key);
}

int main() {
    std::cout << hash("abc") << std::endl;
    return 0;
}
#包括
#包括
#包括
大小\u t散列(常量标准::字符串和键)
{
散列哈希器;
返回哈希器(键);
}
int main(){

std::cout您可以直接使用
std::hash
而不是实现自己的函数

#include <iostream>
#include <functional>
#include <string>

size_t hash(const std::string& key)
{
    std::hash<std::string> hasher;
    return hasher(key);
}

int main() {
    std::cout << hash("abc") << std::endl;
    return 0;
}
#包括
#包括
#包括
大小\u t散列(常量标准::字符串和键)
{
散列哈希器;
返回哈希器(键);
}
int main(){
标准::cout
我使用的是一个可扩展散列,我想把字符串作为键

如前所述,使用
std::hash
,直到有充分理由不这样做

问题是,我正在使用的当前哈希函数迭代整个字符串/键,我认为这是非常糟糕的

这是一个可以理解的想法,但实际上不太可能是一个真正的问题

(期待)为什么

快速扫描堆栈溢出将发现许多有经验的开发人员在谈论缓存和缓存线

(如果我在教我祖母吃蛋,请原谅我)

现代CPU在处理指令和执行(非常复杂的)运算方面速度惊人。在几乎所有情况下,限制其性能的是必须通过总线与内存进行通信,相比之下,总线速度非常慢

因此,芯片设计师们在内存中建立了高速缓存——位于CPU中的极快内存(因此不必通过慢速总线与之通信)。不幸的是,可用空间有限[加上热量限制——这是另一天的话题]对于这个缓存,CPU必须像操作系统对待磁盘缓存一样对待它,在需要时刷新内存并读取内存

如前所述,通过总线进行通信是缓慢的(简单地说),它需要主板上的所有电子元件停止并相互同步。这浪费了大量的时间[讨论电子信号在主板上的传播受到大约一半光速的限制,这将是一个非常好的观点-这很有趣,但这里只有这么多空间,我只有这么多时间]因此,内存不是一次传输一个字节、一个字或一个长字,而是分块访问,称为缓存线

事实证明,这是芯片设计人员的一个好决定,因为他们知道大多数内存是按顺序访问的——因为大多数程序将大部分时间用于线性访问内存(例如计算哈希、比较字符串或对象、转换序列、复制和初始化序列等)

这一切的结果是什么

好吧,奇怪的是,如果你的字符串还没有在缓存中,那么读取一个字节的开销几乎和读取它的前(比如)128个字节中的所有字节的开销一样高

另外,由于缓存电路假定内存访问是线性的,它将在获取第一条缓存线后立即开始获取下一条缓存线。它将在CPU执行哈希计算时执行此操作

我希望您能看到,在这种情况下,即使您的字符串有数千字节长,并且您选择仅散列(比如)每128个字节,您所要做的就是计算一个非常低的哈希值,这仍然会导致内存缓存在获取大量未使用内存时停止处理器。这将花费同样长的时间-导致更糟糕的结果!

话虽如此,有什么好的理由不使用标准实现

仅当:

  • 用户抱怨你的软件太慢,没有用处,并且

  • 该程序是可验证的CPU限制(使用100%的CPU时间),一个