在geeksforgeeks.org的以下网站中,它指出了
链接的缓存性能不佳,因为密钥是使用链表存储的。开放式寻址提供了更好的缓存性能,因为所有内容都存储在同一个表中。
所以我的问题是:
答案 0 :(得分:8)
很抱歉,由于问题非常广泛,答案也会非常通用,有些链接可以提供更详细的信息。
最好从问题开始:
正在使用的缓存在哪里?
在现代CPU上,缓存随处可见:读取程序指令和读取/写入内存中的数据。在大多数CPU上,缓存是透明的,即不需要显式管理缓存。
缓存远比主内存(DRAM)快。为了给你一个观点,访问1级缓存中的数据大约是4个CPU周期,而访问同一CPU上的DRAM大约是200个CPU周期,即快50倍。
缓存在名为缓存行的小块上运行,这些块通常为64字节长。
更多信息:https://en.wikipedia.org/wiki/CPU_cache
导致链接缓存性能不佳的原因是什么?
基本上,链接不是缓存友好的。这不仅仅是哈希表中的这种情况,与“经典”列表相同。
散列密钥(或列表节点)彼此远离,因此每个密钥访问产生“高速缓存未命中”,即慢速DRAM访问。因此,检查链中的10个密钥需要10次DRAM访问,即我们的通用CPU的200 x 10 = 2000
个循环。
在当前密钥中读取next
指针之前,不知道下一个密钥的地址,因此优化空间不大......
为什么开放式寻址会提供更好的缓存性能,因为我无法看到缓存是如何进入的?
线性探测是缓存友好的。密钥“聚集”在一起,因此一旦我们访问第一个密钥(慢速DRAM访问),很可能下一个密钥将已经在缓存中,因为缓存行是64字节。因此,使用开放寻址访问相同的10个密钥需要1个DRAM访问和9个缓存访问,即我们的通用CPU的200 x 1 + 9 x 4 = 236
个周期。对于链式密钥,它比2000个周期快得多。
此外,由于我们以可预测的方式访问内存,因此可以进行优化,例如缓存预取:https://en.wikipedia.org/wiki/Cache_prefetching
在决定链接和线性探测开放寻址和二次探测开放寻址之间时,您还要考虑什么?
无论如何,链接或线性探测都不是一个好兆头。因此,我要考虑的第一件事是通过使用良好的散列函数和合理的散列大小来确保冲突的概率最小。
我要考虑的第二件事是即用型解决方案。当然,当您需要自己的实施时,仍然可能会出现一些罕见的情况......
不确定语言,但这里有使用BSD许可证的快速哈希表实现:http://dpdk.org/browse/dpdk/tree/lib/librte_hash/rte_cuckoo_hash.h
因此,如果你仍然需要自己的哈希表实现并且你关心性能,那么下一个很容易实现的方法是使用缓存对齐的桶而不是普通的哈希元素。每个元素将浪费几个字节(即每个哈希表元素将长64个字节),但是在发生冲突的情况下,将存在至少几个密钥的一些快速存储。管理这些存储桶的代码也会更复杂一些,所以要考虑是否值得你去打扰......