`
febird
  • 浏览: 256305 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

缓存与平行数组在 hash_strmap 和 gold_hash_map 中的应用

 
阅读更多

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 带来的好处太小了,不值得付出那么多努力。

总结
使用了这么多的技巧和设计思想,结果如何呢?gold_hash_map 完败几乎所有 hash map 的实现
分享到:
评论

相关推荐

    hash_map的详解

    在这个例子中,我们定义了`ClassA`类作为键,并且定义了`hash_A`和`equal_A`结构体来分别实现哈希函数和比较函数。这样就可以根据类的属性值来存储和查找信息。 #### 3. 总结 `hash_map`是一种非常强大的数据结构...

    c++中hash_table以及std::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的简单应用

    hash_map

    linux hash_map

    1. **包含头文件**:在C++标准库中,`hash_map`并不直接包含在`<map>`中,而是位于`<ext/hash_map>`。因此,使用`hash_map`时需要包含这个头文件,并使用`__gnu_cxx::hash_map`来引用它。 2. **模板参数**:`hash_...

    Hash_map 实现源码

    在`HashMap.cpp`中,可能会定义一个哈希函数模板,如`template<typename Key> size_t hash_function(const Key& key)`,用于计算键的哈希值。 接下来是哈希表的结构。通常,哈希表是一个数组,每个元素是一个链表或...

    C++哈希表使用的好文章-Hash_Map

    在这个例子中,`hash_map`的类型参数分别为key的类型、value的类型、自定义的哈希函数和比较函数。`hash<std::string>`是预定义的哈希函数,`std::equal_to<std::string>`是默认的比较函数,它们适用于大部分情况。...

    Hash值查看以及修改软件(Hash_1.0.4_0523.exe以及HashModifier.exe)

    总的来说,"Hash_1.0.4_0523.exe"和"HashModifier.exe"是IT工作中的重要辅助工具,它们可以帮助用户快速检查文件的Hash值,确保文件的完整性和一致性,同时也可以在特定场景下改变文件内容以达到特定目的。...

    gen_lex_hash_pc

    在实际操作中,使用`gen_lex_hash_5.1.51_pc`或`gen_lex_hash_5.1.63_pc`进行交叉编译通常包含以下步骤: 1. 配置编译环境,确保拥有正确的交叉编译工具链。 2. 解压并进入`gen_lex_hash_5.1.x_pc`目录。 3. 使用...

    TBB并发容器 学习笔记

    3. `concurrent_hash_map`:这是一个线程安全的哈希映射,允许并发的读写操作。它在多线程环境下提供高效的数据查找和更新,同时确保数据一致性。 相比于传统的STL容器,TBB并发容器的优势在于它们在设计时考虑了多...

    hash.rar_HASH算法_fpga hash_hash_zebra85v_哈希表Verilog

    标题中的“hash.rar_HASH算法_fpga hash_hash_zebra85v_哈希表Verilog”揭示了这个压缩包文件的主要内容,它涉及到哈希(Hash)算法在高速Field-Programmable Gate Array(FPGA)上的实现,以及与Zebra85v硬件平台和...

    asp版hmac_sha1加密方式,真正和PHP的hash_hmac加密结果完全一样。支持中文utf-8编码

    在 ASP 和 PHP 环境中,实现 HMAC_SHA1 加密方法对于确保跨平台应用的数据一致性尤为关键。本文将深入探讨 ASP 版本的 HMAC_SHA1 加密,如何与 PHP 的 `hash_hmac` 函数保持结果一致,以及如何处理中文 UTF-8 编码。...

    dense_hash_map:STD的简单替代

    jg :: dense_hash_map 一个简单的std::unordered_map替代品,具有更好的性能,但失去了稳定的寻址方式,这是一种折衷方案。 在此处查看此哈希图的详细说明: : 生成状态: 特拉维斯(Travis):

    哈希映射 hash map

    `hash_map`与标准库中的`map`容器相似,都是用来管理键值对,但`hash_map`通常比`map`更快,因为`map`基于红黑树,查找和插入的时间复杂度是O(log n)。 当我们使用`hash_map`时,我们需要提供两个关键组件:哈希...

    HASH_hash_stm32hash_stm32hash表_stm32f407_

    在STM32F407上实现的哈希(Hash)算法是数字签名、数据完整性验证等安全应用中的关键组成部分。哈希算法能够将任意长度的输入数据转化为固定长度的输出,通常称为哈希值或消息摘要。 哈希算法的主要特性包括: 1. *...

    文件MD5 ...查看 Hash_1.0.4_XiaZaiBa.exe

    在压缩包子文件的文件名称列表中,只有一个文件:“Hash_1.0.4_XiaZaiBa.exe”,这表明压缩包内包含的是该MD5查看工具的可执行文件。 现在我们详细探讨一下MD5哈希及其应用: MD5哈希计算是一种单向函数,即给定...

    MD5校验Hash_1.0.4

    在软件分发过程中,开发者通常会提供软件的MD5校验码,用户下载软件后,可以通过MD5校验Hash_1.0.4这样的工具计算下载文件的MD5值,并与开发者提供的值进行比较。如果两者一致,说明文件在传输过程中没有发生错误,...

    cpp-sparsemap一个高效hashmap和hashset的C实现

    《C++ Sparse Map:高效的哈希映射与集合实现解析》 在计算机科学中,数据结构的选择对于程序的性能有着至关重要的影响。特别是在C++这样的系统级编程语言中,高效的数据结构更是程序员们的得力助手。本文将深入...

    Hash_1.0.4_0523.zip

    标题中的“Hash_1.0.4_0523.zip”是一个压缩包文件,其中包含了名为“Hash_1.0.4_0523.exe”的应用程序,这是一款专注于进行哈希值计算的小工具,特别适用于快速校验文件的完整性。其1.0.4的版本号和0523的日期标记...

    key是string的hash_map

    本实例实现了一个hash_map,key是string类型,即可以存储索引是string的数据,希望对大家有帮助

    hash_XE2_by_eGust.zip

    《深入理解Delphi中的哈希算法:...通过对eGust的hash_XE2算法的解析和实践,我们可以增强对哈希算法的理解,学习如何在Delphi环境中实现和应用此类算法。这不仅有助于提升编程技能,也能为解决特定问题提供新的思路。

Global site tag (gtag.js) - Google Analytics