2007 年我写过一篇关于平行数组与CPU缓存文章,最近,我在 hash_strmap 和 gold_hash_map 中应用了这种设计思想。
hash value cache
对于一些对象,计算它们的 hash value 很昂贵,而对于另外一些对象,计算它们的 hash value 很廉价;所以,我们是否做 hash 缓存对 hash table 性能很重要。
更有意思的是,hash 缓存一般只在 hash table 的增长阶段访问(rehash),而在查找阶段,不需要访问 hash 缓存,于是,可以把 hash cache 从数据结构的结点中提取出来,放到一个分离的平行数组中。所谓平行数组,也就是:以前是访问 pNode[i].hash,现在是 pHash[i],很简单的概念。
更有意思的是,我们可以随时启用(enable)或禁用(disable) hash 缓存,在 hash_strmap 的测试中,启用它大约会使插入操作的平均性能提升一倍。具体到实现上,我们甚至不需要额外的变量来表示 hash 缓存是否启用,只需要将 pHash 设成一个特殊的值来表达它是禁用的,现在设的是整数 8 (与“不”谐音),当然需要强制类型转换pHash = (size_t*)(8)。
link array
这个仅使用在 gold_hash_map 中,因为在 hash_strmap 中,link 和 offset (指向 strpool 的偏移)一起占用 8 字节(在64位环境中刚好一个指针宽度)。
hash table 使用开地址解决 hash 冲突,这样在同一个桶(bucket) 中的所有 Node 是用一个单向链表链接起来的,传统实现是使用指针做链接,gold_hash_map 和 hash_strmap 使用数组下标做链接,这样不但可以在任何时候重新链接(比如gold_hash_map 和 hash_strmap 都支持排序,而排序打乱了数组下标,所以排序之后需要重新链接),并且,在 64 位环境中还可以节约一半的内存。
传统的做法是将 link 作为 Node 的一个成员,这样很简单也很自然,gold_hash_map 一开始就是这样实现的,然而,仔细分析我们会发现,在 hash table 的查找中,对 link 访问的概率是很低的,请看代码:
只有在桶(bucket)中得到的第一个 Node 不匹配 key 的时候,才需要访问 link,而在 hash table 中,这样的第一个 Node 不匹配 Key 的概率是相对很小的,并且把它控制得很小是一个 hashfunc 优劣的评价标准。
另一方面,虽然内存现在很白菜价,并且会继续更加白菜价,然而CPU cache仍然非常昂贵。如果link和data成员都放在Node中,CPU加载data (代码中的Elem)时也会加载link,而加载进来的link却很少会被访问到。这就是为什么要把link分出去。
将link分出去还带来了一个额外的好处:hash cache 分出去了,link 也分出去了,Node 中现在就只剩下 data 了,Node 就没有存在的必要了,Node数组就是 data 数组了!这样完全省去了专门写 iterator 的代码,直接将 iterator 定义成指针就OK了。
查找失败时
查找总有找不到的时候,当查找的 Key 不存在的时候,并且 h%nBucket 刚好打进一个不空的桶:
在上面 find_i 函数中,调用 equal 之前先判断一下 hash 缓存会更快,因为在随机情况下h1%n == h2%n 时,h1 != h2 的可能性要大得多。
并且,在这种情况下,需要遍历完整个 linked list 才能知道 Key 真的不存在,所以此时至少需要访问一次 pLink 数组。
似乎此时变得很坏!然而再仔细分析上面的条件:只有在 Key 不存在,并且并且h%nBucket刚好打进一个不空的桶!如果我们把 hash tab 的装载因子 (load factor)设得小一些,比如(0.3),这个概率就会变小,从而对 pLink 数组的访问需求也变小了。因为 link 用的是整数,所以,如果我们知道整个 hash tab 不会装太多东西,我们可以把 LinkTp 设成较小的整数类型(比如
unsigned short,16位,这样最多可以在 gold_hash_tab 中装入 65535 条记录)。从而,相同的内存空间,我们可以把装载因子调得更小,从而使得访问 pLink 数组的概率更小,并且 hash 冲突的概率也更小了。
hash_strmap 的 LinkTp 是固定的(unsigned int),无法改变,这是刻意为之,因为这种灵活性对 hash_strmap 带来的好处太小了,不值得付出那么多努力。
总结
分享到:
相关推荐
在这个例子中,我们定义了`ClassA`类作为键,并且定义了`hash_A`和`equal_A`结构体来分别实现哈希函数和比较函数。这样就可以根据类的属性值来存储和查找信息。 #### 3. 总结 `hash_map`是一种非常强大的数据结构...
代码重点是hash_table,附加std::map与其做对比,实现的是一条sql语句:select c_nationkey, c_mktsegment, count(*), max(c_acctbal) from aaa_customer_1g group by c_nationkey, c_mktsegment order by c_...
hash_map
1. **包含头文件**:在C++标准库中,`hash_map`并不直接包含在`<map>`中,而是位于`<ext/hash_map>`。因此,使用`hash_map`时需要包含这个头文件,并使用`__gnu_cxx::hash_map`来引用它。 2. **模板参数**:`hash_...
在`HashMap.cpp`中,可能会定义一个哈希函数模板,如`template<typename Key> size_t hash_function(const Key& key)`,用于计算键的哈希值。 接下来是哈希表的结构。通常,哈希表是一个数组,每个元素是一个链表或...
在这个例子中,`hash_map`的类型参数分别为key的类型、value的类型、自定义的哈希函数和比较函数。`hash<std::string>`是预定义的哈希函数,`std::equal_to<std::string>`是默认的比较函数,它们适用于大部分情况。...
总的来说,"Hash_1.0.4_0523.exe"和"HashModifier.exe"是IT工作中的重要辅助工具,它们可以帮助用户快速检查文件的Hash值,确保文件的完整性和一致性,同时也可以在特定场景下改变文件内容以达到特定目的。...
在实际操作中,使用`gen_lex_hash_5.1.51_pc`或`gen_lex_hash_5.1.63_pc`进行交叉编译通常包含以下步骤: 1. 配置编译环境,确保拥有正确的交叉编译工具链。 2. 解压并进入`gen_lex_hash_5.1.x_pc`目录。 3. 使用...
3. `concurrent_hash_map`:这是一个线程安全的哈希映射,允许并发的读写操作。它在多线程环境下提供高效的数据查找和更新,同时确保数据一致性。 相比于传统的STL容器,TBB并发容器的优势在于它们在设计时考虑了多...
标题中的“hash.rar_HASH算法_fpga hash_hash_zebra85v_哈希表Verilog”揭示了这个压缩包文件的主要内容,它涉及到哈希(Hash)算法在高速Field-Programmable Gate Array(FPGA)上的实现,以及与Zebra85v硬件平台和...
在 ASP 和 PHP 环境中,实现 HMAC_SHA1 加密方法对于确保跨平台应用的数据一致性尤为关键。本文将深入探讨 ASP 版本的 HMAC_SHA1 加密,如何与 PHP 的 `hash_hmac` 函数保持结果一致,以及如何处理中文 UTF-8 编码。...
jg :: dense_hash_map 一个简单的std::unordered_map替代品,具有更好的性能,但失去了稳定的寻址方式,这是一种折衷方案。 在此处查看此哈希图的详细说明: : 生成状态: 特拉维斯(Travis):
`hash_map`与标准库中的`map`容器相似,都是用来管理键值对,但`hash_map`通常比`map`更快,因为`map`基于红黑树,查找和插入的时间复杂度是O(log n)。 当我们使用`hash_map`时,我们需要提供两个关键组件:哈希...
在STM32F407上实现的哈希(Hash)算法是数字签名、数据完整性验证等安全应用中的关键组成部分。哈希算法能够将任意长度的输入数据转化为固定长度的输出,通常称为哈希值或消息摘要。 哈希算法的主要特性包括: 1. *...
在压缩包子文件的文件名称列表中,只有一个文件:“Hash_1.0.4_XiaZaiBa.exe”,这表明压缩包内包含的是该MD5查看工具的可执行文件。 现在我们详细探讨一下MD5哈希及其应用: MD5哈希计算是一种单向函数,即给定...
在软件分发过程中,开发者通常会提供软件的MD5校验码,用户下载软件后,可以通过MD5校验Hash_1.0.4这样的工具计算下载文件的MD5值,并与开发者提供的值进行比较。如果两者一致,说明文件在传输过程中没有发生错误,...
《C++ Sparse Map:高效的哈希映射与集合实现解析》 在计算机科学中,数据结构的选择对于程序的性能有着至关重要的影响。特别是在C++这样的系统级编程语言中,高效的数据结构更是程序员们的得力助手。本文将深入...
标题中的“Hash_1.0.4_0523.zip”是一个压缩包文件,其中包含了名为“Hash_1.0.4_0523.exe”的应用程序,这是一款专注于进行哈希值计算的小工具,特别适用于快速校验文件的完整性。其1.0.4的版本号和0523的日期标记...
本实例实现了一个hash_map,key是string类型,即可以存储索引是string的数据,希望对大家有帮助
《深入理解Delphi中的哈希算法:...通过对eGust的hash_XE2算法的解析和实践,我们可以增强对哈希算法的理解,学习如何在Delphi环境中实现和应用此类算法。这不仅有助于提升编程技能,也能为解决特定问题提供新的思路。