链式与开放式寻址的哈希表中的缓存性能
Posted
技术标签:
【中文标题】链式与开放式寻址的哈希表中的缓存性能【英文标题】:Cache Performance in Hash Tables with Chaining vs Open Addressing 【发布时间】:2018-09-17 11:46:00 【问题描述】:在geeksforgeeks.org 的以下网站中声明
链接的缓存性能不好,因为键是使用链表存储的。开放寻址提供更好的缓存性能,因为所有内容都存储在同一个表中。
所以我的问题是:
是什么导致链式缓存性能不佳? 缓存在哪里使用? 为什么开放寻址会提供更好的缓存性能,因为我看不到缓存是如何进入其中的? 在决定链接和线性探测开放寻址和二次探测开放寻址时,您还考虑了哪些因素?【问题讨论】:
【参考方案1】:抱歉,由于问题非常广泛,答案也将非常笼统,并提供一些链接以获取更详细的信息。
最好从问题开始:
缓存在哪里使用?
在现代 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
个周期。
直到在当前key中读取到next
指针后才知道下一个key的地址,所以没有太大的优化空间……
为什么开放寻址会提供更好的缓存性能,因为我看不到缓存是如何进入其中的?
线性探测是缓存友好的。键是“聚集”在一起的,所以一旦我们访问了第一个键(慢速 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 字节),但在发生冲突的情况下,至少会为几个键提供一些快速存储。管理这些存储桶的代码也会稍微复杂一些,因此值得您考虑是否值得费心……
【讨论】:
以上是关于链式与开放式寻址的哈希表中的缓存性能的主要内容,如果未能解决你的问题,请参考以下文章