【发布时间】:2013-09-27 03:48:24
【问题描述】:
我有一个存储四叉树条目的哈希表。
哈希函数如下所示:
四叉树散列
#define node_hash(a,b,c,d) \
(((int)(d))+3*(((int)(c))+3*(((int)(b))+3*((int)(a))+3)))
请注意,此操作的结果总是使用模素数进行分块,如下所示:
h = node_hash(p->nw, p->ne, p->sw, p->se) ;
h %= hashprime ;
...
与最佳哈希的比较
一些统计分析表明,这种哈希在减少冲突方面是最佳的。
给定一个带有 b 存储桶和 n 条目的哈希表。使用完美哈希的碰撞风险为:(n - b * (1 - power((b-1)/b,n)))) * 100 / n
当 n = b 时,这意味着 37% 的碰撞风险。
一些测试表明,上述哈希值与规范非常吻合(对于哈希表的所有填充级别)。
运行时间
运行时严重依赖hashprime 的值
时间(1000 次运行中最好的)是:
hashprime CPU-cycles per run
--------------------------------
4049 56
16217 68
64871 127 <-- whoooh
有没有办法改善这一点,同时仍保持最佳的碰撞风险?
通过优化模运算(将其替换为在循环外使用“魔术”数字计算机的乘法)。
用其他哈希函数替换哈希函数。
背景
生成以下程序集:
//--------h = node_hash(p->nw, p->ne, p->sw, p->se) ;
mov eax,[rcx+node.nw] <<+
lea eax,[eax+eax*2+3] |
add eax,[rcx+node.ne] |
lea eax,[eax+eax*2] +- takes +/- 12 cycles
add eax,[rcx+node.sw] |
lea eax,[eax+eax*2] |
add eax,[rcx+node.se] <<+
//--------h %= hashprime ;
mov esi,[hashprime]
xor edx,edx
div esi
mov rax,rdx <<--- takes all the rest
[编辑]
我也许可以用以下事实做点什么:
C = A % B 等价于C = A – B * (A / B)
利用整数除法与乘以倒数相同的事实。
从而将公式转换为C = A - B * (A * rB)
请注意,对于整数除法,倒数是幻数,请参阅:http://www.hackersdelight.org/magic.htm
C代码在这里:http://web.archive.org/web/20070713211039/http://hackersdelight.org/HDcode/magic.c
[FNV 哈希]
见:http://www.isthe.com/chongo/tech/comp/fnv/#FNV-1a
hash = offset_basis
for each byte to be hashed
hash = hash xor octet_of_data
hash = hash * FNV_prime (for 32 bits = 16777619)
return hash
对于截断为 32 位 = 16 字节的 4 个指针,FNV 哈希需要 27 个周期(手工组装)
不幸的是,这会导致 81% 的哈希冲突,而应该是 37%。
运行完整的 15 次乘法需要 179 个周期。
【问题讨论】:
-
你确定 40 个额外的时钟周期确实是个问题吗?
-
是的,因为这个哈希函数在小型工作负载上执行了 1300 万次,而在大型工作负载上执行了许多数量级。它是最常被称为例程的。占总运行时间的 20%。
-
如何使用
unsigned int而不是int并让值回滚。相当于hashprime的值UINT_MAX不是吗? -
或许可以尝试在math.stackexchange.com 上提问?
-
@pwny,hashprime 是具有大小为
Uint_max的哈希表的哈希表的大小,在 64 位系统上会 bad :-) 除了 hashprime 需要一个素数,以便有效地工作。 (参见comparison with optimal hash上的段落)。
标签: c++ c math optimization