散列函数为64位到10位

时间:2012-07-06 09:25:21

标签: c linux gcc hash x86-64

我想要一个带有长数(64位)的哈希函数并产生10位的结果。为此目的,最好的哈希函数是什么。输入基本上是变量的地址(地址在Linux上是64位或8字节),所以我的哈希函数应该为此目的进行优化。

3 个答案:

答案 0 :(得分:6)

我会说像这样的事情:

uint32_t hash(uint64_t x)
{
    x >>= 3;
    return (x ^ (x>>10) ^ (x>>20)) & 0x3FF;
}

至少有效3位不是很有用,因为大多数变量是4字节或8字节对齐,所以我们删除它们。 然后我们采用接下来的30位并将它们混合在一起(XOR),每块10位。

当然,您也可以选择(x>>30)^(x>>40)^(x>>50),但我不确定它们在实践中是否会有所不同。

答案 1 :(得分:1)

对于大多数发行版而言,最好是按素数修改,1021是最大的10位素数。没有必要剥离低位。

static inline int hashaddress(void *v)
{
        return (uintptr_t)v % 1021;
}

如果您认为性能可能是一个问题,请手头有一些替补,并在实际程序中将它们与进行比赛。微型车标志是浪费;几个周期的差异几乎肯定会被缓存效应所淹没,并且尺寸很重要。

答案 2 :(得分:1)

我写了一个玩具程序查看堆栈,数据区和堆上的一些真实地址。基本上我宣布4个全局,4个本地人并且做了2个mallocs。打印地址时,我丢弃了最后两位。以下是其中一个运行的 输出:

 20125e8
 20125e6
 20125e7
 20125e4
3fef2131
3fef2130
3fef212f
3fef212c
 25e4802
 25e4806

这告诉我的是什么:

  1. 此输出中的LSB(地址的第3位)经常“打开”“关闭”。所以我不会在计算哈希时放弃它。降低2个LSB就足够了。
  2. 我们也看到低8-10位有更多的熵。在计算哈希值时,我们必须使用
  3. 我们知道在64位计算机上virtual addresses are never more than 48 bits wide
  4. 我接下来会做什么

    /* Drop two LSBs.  */
    a >>= 2;
    
    /* Get rid of the MSBs. Keep 46 bits. */
    a &= 0x3fffffffffff;
    
    /* Get the 14 MSBs and fold them in to get a 32 bit integer.
    The MSBs are mostly 0s anyway, so we don't lose much entropy.  */
    msbs = (a >> 32) << 18;
    a ^= msbs;
    

    现在我们通过decent 'half avalanche' hash function传递此内容,而不是滚动我们自己的。 “半雪崩”意味着输入的每一位都有可能影响同一位置的位和更高

    uint32_t half_avalanche( uint32_t a)
    {
        a = (a+0x479ab41d) + (a<<8);
        a = (a^0xe4aa10ce) ^ (a>>5);
        a = (a+0x9942f0a6) - (a<<14);
        a = (a^0x5aedd67d) ^ (a>>3);
        a = (a+0x17bea992) + (a<<7);
        return a;
    }
    

    对于10位散列,请使用返回的uint32_t的10个MSB。如果您为N位哈希选择N MSB,则哈希函数继续正常,有效地将每个附加位的桶计数加倍。

    我有点无聊,所以我为此写了一个玩具基准。 没什么好看的,它在堆上分配一堆内存并尝试哈希我如上所述。来源可以来自 here 。一个示例结果:

      

    1024个桶,产生256个值,29个碰撞
      1024个桶,生成512个值,103个转换
      1024个桶,生成1024个值,370个收获

    下一步:我尝试了另外两个哈希在这里回答。他们都有类似的表现。看起来像:选择最快的一个;)