具有链接与开放寻址的哈希表中的缓存性能
Cache Performance in Hash Tables with Chaining vs Open Addressing
在以下来自 geeksforgeeks.org 的网站中,它指出
Cache performance of chaining is not good as keys are stored using linked list. Open addressing provides better cache performance as everything is stored in same table.
所以我的问题是:
- 是什么导致链接缓存性能不佳?
- 缓存在哪里使用?
- 为什么开放寻址会提供更好的缓存性能,因为我看不到缓存是如何产生的?
- 在决定链接和线性探测开放寻址与二次探测开放寻址之间时,您还考虑了哪些因素?
抱歉,由于问题范围很广,答案也将非常笼统,并提供一些链接以获取更详细的信息。
最好从问题开始:
Where is the cache being used?
在现代 CPU 年代,缓存无处不在:读取内存中的程序指令和 read/write 数据。大多数 CPU 缓存是透明的,即不需要显式管理缓存。
缓存比主内存 (DRAM) 快 很多。只是为了给你一个视角,访问一级缓存中的数据需要 ~4 CPU 个周期,而在同一 CPU 上访问 DRAM 需要~200 CPU 个周期,即快了 50 倍。
缓存在称为 缓存行 的小块上运行,这些小块通常为 64 字节长。
更多信息:https://en.wikipedia.org/wiki/CPU_cache
What causes chaining to have a bad cache performance?
基本上,链接对缓存不友好。这不仅与散列 table 中的这种情况有关,与 "classical" 列表的问题相同。
哈希键(或列表节点)彼此相距较远,因此每次访问键都会产生一个"cache miss",即慢速DRAM访问。因此,检查链中的 10 个密钥需要 10 次 DRAM 访问,即 200 x 10 = 2000
个周期用于我们的通用 CPU.
只有在当前键中读取next
指针后才能知道下一个键的地址,因此没有太大的优化空间...
Why would open addressing provide better cache performance as I cannot see how the cache comes into this?
线性探测是缓存友好的。键是 "clustered" 在一起,所以一旦我们访问了第一个键(慢速 DRAM 访问),下一个键很可能已经在缓存中,因为缓存行是 64 字节。因此,使用开放寻址访问相同的 10 个密钥需要 1 次 DRAM 访问和 9 次高速缓存访问,即 200 x 1 + 9 x 4 = 236
个周期用于我们的通用 CPU。它比链式密钥的 2000 次循环快得多。
此外,由于我们以 predictable 方式访问内存,因此还有缓存预取等优化空间:https://en.wikipedia.org/wiki/Cache_prefetching
Also what considerations what you take into account when deciding between chaining and linear probed open addressing and quadratic probed open addressing?
链接或线性探测无论如何都不是好兆头。所以我首先要考虑的是通过使用好的散列函数和合理的散列大小来确保冲突的可能性最小。
我要考虑的第二件事是准备好使用的解决方案。当然,当您需要自己的实现时,仍然可能存在一些罕见的情况...
不确定语言,但这里是具有 BSD 许可证的超快散列 table 实现:http://dpdk.org/browse/dpdk/tree/lib/librte_hash/rte_cuckoo_hash.h
因此,如果您仍然需要自己的哈希 table 实现并且您确实关心性能,那么下一个很容易实现的事情就是使用缓存对齐的桶而不是普通的哈希元素。每个元素会浪费几个字节(即每个 hash table 元素将有 64 个字节长),但在发生冲突的情况下,至少会有一些键的快速存储。管理这些桶的代码也会稍微复杂一些,所以值得你考虑一下...
在以下来自 geeksforgeeks.org 的网站中,它指出
Cache performance of chaining is not good as keys are stored using linked list. Open addressing provides better cache performance as everything is stored in same table.
所以我的问题是:
- 是什么导致链接缓存性能不佳?
- 缓存在哪里使用?
- 为什么开放寻址会提供更好的缓存性能,因为我看不到缓存是如何产生的?
- 在决定链接和线性探测开放寻址与二次探测开放寻址之间时,您还考虑了哪些因素?
抱歉,由于问题范围很广,答案也将非常笼统,并提供一些链接以获取更详细的信息。
最好从问题开始:
Where is the cache being used?
在现代 CPU 年代,缓存无处不在:读取内存中的程序指令和 read/write 数据。大多数 CPU 缓存是透明的,即不需要显式管理缓存。
缓存比主内存 (DRAM) 快 很多。只是为了给你一个视角,访问一级缓存中的数据需要 ~4 CPU 个周期,而在同一 CPU 上访问 DRAM 需要~200 CPU 个周期,即快了 50 倍。
缓存在称为 缓存行 的小块上运行,这些小块通常为 64 字节长。
更多信息:https://en.wikipedia.org/wiki/CPU_cache
What causes chaining to have a bad cache performance?
基本上,链接对缓存不友好。这不仅与散列 table 中的这种情况有关,与 "classical" 列表的问题相同。
哈希键(或列表节点)彼此相距较远,因此每次访问键都会产生一个"cache miss",即慢速DRAM访问。因此,检查链中的 10 个密钥需要 10 次 DRAM 访问,即 200 x 10 = 2000
个周期用于我们的通用 CPU.
只有在当前键中读取next
指针后才能知道下一个键的地址,因此没有太大的优化空间...
Why would open addressing provide better cache performance as I cannot see how the cache comes into this?
线性探测是缓存友好的。键是 "clustered" 在一起,所以一旦我们访问了第一个键(慢速 DRAM 访问),下一个键很可能已经在缓存中,因为缓存行是 64 字节。因此,使用开放寻址访问相同的 10 个密钥需要 1 次 DRAM 访问和 9 次高速缓存访问,即 200 x 1 + 9 x 4 = 236
个周期用于我们的通用 CPU。它比链式密钥的 2000 次循环快得多。
此外,由于我们以 predictable 方式访问内存,因此还有缓存预取等优化空间:https://en.wikipedia.org/wiki/Cache_prefetching
Also what considerations what you take into account when deciding between chaining and linear probed open addressing and quadratic probed open addressing?
链接或线性探测无论如何都不是好兆头。所以我首先要考虑的是通过使用好的散列函数和合理的散列大小来确保冲突的可能性最小。
我要考虑的第二件事是准备好使用的解决方案。当然,当您需要自己的实现时,仍然可能存在一些罕见的情况...
不确定语言,但这里是具有 BSD 许可证的超快散列 table 实现:http://dpdk.org/browse/dpdk/tree/lib/librte_hash/rte_cuckoo_hash.h
因此,如果您仍然需要自己的哈希 table 实现并且您确实关心性能,那么下一个很容易实现的事情就是使用缓存对齐的桶而不是普通的哈希元素。每个元素会浪费几个字节(即每个 hash table 元素将有 64 个字节长),但在发生冲突的情况下,至少会有一些键的快速存储。管理这些桶的代码也会稍微复杂一些,所以值得你考虑一下...