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

hash_strmap 为什么那么快

 
阅读更多

测试结果(普通PC,CPU 3G HZ,内存2G)

  • 单核达到了每秒30,000,000次查询,string 长度是32字节。
  • iteration 的速度, 比 std::map 快180倍以上, 比 unordered_map 快150倍。

同样是 Hash Map,为什么 hash_strmap 的查询速度可以比标准 unordered_map<string...> 快 10 倍?比 std::map (这个就不是hash map了)快40倍?超大数据量测试对比更明显,达到了12倍和50倍。并且,内存占用量还要小一倍以上?


链接

关键技术


使用大块内存

  • 整个 map 只需要 3 或 4(ValueOut 时,见下)块连续内存
    1. bucket 数组: unsigned int* bucket; // 32 位,4 字节
    2. Node 数组: 需要多分配一个数组元素。
      多的那个Node,其 offset 用来计算最后一个 strlen,link 用来做 iterator.++ 的 guard
    3. strpool
    4. values,如果是 ValueOut,Value/mapped_type 将保存在这个数组,与 Node 数组是平行的
  • 而如果使用标准的 hash map 来做 string map,需要大量的小块内存
  • hash 开地址链接使用整数数组下标,而传统的做法是使用指针来链接
    • 指针做链接在64位平台中是8个字节
    • 数组下标作链接,只需要32位,可以表达 4G-2 个结点,一般每个结点的尺寸总大于16字节(64位平台),即使 wordcount,结点尺寸也至少需要20个字节,从而结点数组的最大空间可以达到 80G。这几乎可以满足所有应用了。
  • 尽可能使用 realloc,原因如下
    1. 使代码更加简洁
    2. 现代的 realloc, 特别是 linux 下,即使返回的地址与传入的不同,也可能不需要拷贝内存,因为实现上可以使用 mremap 来重新将那块物理内存的虚拟地址映射到另一个有更大连续空间的虚拟地址,当内存块很大时,性能的提升是非常可观的。而使用 malloc+memcpy 或者 std::vector 就没有这个优势了。具体可以去google搜索一下相关资料。

String Pool

  • 所有 string 的内容保存在一大块连续的内存中,避免了小块内存分配的时间和空间开销
  • 每个 string 由指向 string pool 的 offset 确定,而非一个指针
  • 每个 string 的长度由相邻两个 offset 想减得到,这样可以省去专门存储长度的内存开销(offset 数组末尾需要多加一个元素)
  • 对齐,对齐的数据在访问时有非常良好的性能,并且,可以一次处理多个字节(64 bit 中是 8 个字节)
  • offset 和对齐是高度相关的并且接合起来有额外好处
    • 即使在 64 位平台上,offset 也只有 4 个字节,32 位,从而,最大的寻址空间是4G (实际上是 4G-1),所以 string pool 中的 string 总长度不能超过 4G
    • 如果对齐了,那么最低的 2 位(4 bytes 对齐)或 3 位(8 bytes 对齐)总是 0,所以,我就把实际的 offset (offset 在 64 位平台下是 64 位)除以 4 或者 8 再存储,这样,在64位平台中最大的寻址空间就多了 8 倍,达到 32G(实际上是 32G-8),再加上结点和桶空间,可以耗用120G的内存。这几乎可以满足所有的应用了。
    • 另一个好处,如果编译器足够智能,它可以判断出 string pool base ptr + offset 是按 8 字节对齐的,从而 memcpy 时可以优化。

compile time dispatch

  • ValueInline/ValueOut
    • 如果 mapped_type (也就是 Value) 太大,或者其它原因,我们希望 Value 分开存储到一个平行的数组中,需要使用 ValueOut
    • ValueOut 还有一个额外的好处:因为 hash_strmap 支持排序/二分查找(不需要额外空间),如果是按 Value 排序,二分查找时 ValueOut 将导致更好地内存局部性
    • ValueOut 还有另一个额外的好处:当 enlarge (类似 vector 的enlarge)时,如果 Value 不能 memcpy (下面将提到的 FastCopy),Node 数组仍然可以使用 realloc
  • SafeCopy/FastCopy
    • 大部分对象都可以直接 memcpy 到另一个内存地址并毫无问题地直接使用(C++11 中得 std::move 是这样的一个泛化)
      • 简单的对象如各种基本类型,复杂点的如 std::string, std::vector, std::deque 等,当然 hash_strmap 是可以 memcpy 的
    • 什么样的对象memcpy 到另一个内存地址就不能正确使用了呢?原则讲只有一种:自己或内嵌对象的地址被别的东西引用了!
      • 比如说 std::list, list 为了优化,内嵌了一个 partial node: 仅包含 prev/next 字段的 node,空 list 的 partial node 的 prev/next 都指向partial node 本身,list::end() 返回的 iterator 实际上就是该 partial node.
      • 再比如说 ostringstream,它是个很大的对象(在 64bit gnu c++ 是 352 字节,44 ptr),其基类包含一个指向 streambuf 的指针,为了避免申请多余的动态空间,ostringstream 将自己的streambuf内嵌在自身。
      • std::map/set 等也有可能这样实现。

元素删除

  • 可以使用非常复杂的内存管理策略来实现删除,但是因为硬性限制:string length 由 offset 想减获得。使得即使 string pool 内存管理很牛逼了,Node 数组的管理仍然会很复杂。因为 string 长度不固定的,这样的复杂内存管理,其能力已接近通用的malloc/free了。
  • 所以使用非常简单的策略,其平摊复杂度是O(avgstrlen),如果认为avgstrlen(字符串的平均长度)是个常数,这个复杂度就是O(1)
  • 删除元素会在 Node 数组和 string pool 中形成空洞,删除标识用 link 域来表达。
  • 删除的数目达到一定阈值(目前是达到一半)时,压缩并回收这些空洞

Guard / Past The End

  • 为计算 string 长度,string 的 offset 需要多一个元素
  • 为加速 iterator,link 域需要多一个元素

排序

  • 传统的 hash map 不能支持排序,如果非要支持,就需要一个额外的数组来实现。
  • 因为 hash_strmap 的 hash 链接是用 数组下标 来实现的,排序就太简单了,不需要额外的空间,代码也很简单,并且可以按Key 或 Value 排序,大部分情况下,即使在排序过程中也不需要额外的空间。
  • 排序前先要回收删除元素以消除空洞,如果是按 Key 排序,因为 Key string length 是由 offset 想减得到的,排序时顺序被打乱,就无法得到 正确的 string length,而排序后总是需要 relink 的,从而可以事先将 string length 算出来存在 link 域,排序时就可以使用。
  • 按 Key 排序还有一个额外好处,如果对 Key 的访问按字典序有局部性,即使不用二分查找而使用 hash 进行精确查找,性能也有不少提高
    • 对 bucket 的访问仍然是随机的,但是对 bucket[...] 指向的 Node(包含KeyValue) 的访问有这种局部性
  • 其它更详细的,可以参见代码

其它

hash_strmap 删除一个元素时,有可能导致指向其它元素的 iterator 失效,这是与 std::map 和 unordered_map 相比之下的缺点
分享到:
评论

相关推荐

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

    Hash值查看及修改软件,如"Hash_1.0.4_0523.exe"和"HashModifier.exe",是专门用来计算和可能修改文件Hash值的实用工具,对于验证文件的完整性和一致性具有重要意义。 首先,让我们深入了解什么是Hash值。Hash函数...

    gen_lex_hash_pc

    "gen_lex_hash_pc"便是这样一款专为PC平台进行MySQL交叉编译而设计的工具,它包含了5.1.51和5.1.63两个关键版本,但遗憾的是,5.1.67版本未能成功编译。 `gen_lex_hash`是MySQL源码树中的一个子程序,其主要功能是...

    hash_map的详解

    为什么需要hash_map? 在软件开发中,经常会遇到需要高效存储和查找键值对(key-value)的情况。例如,在管理人物名称及其相关信息时,我们希望能够快速地添加、查找和更新数据。传统的做法是使用数组或链表,但这会...

    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

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

    查看 Hash_1.0.4_XiaZaiBa.exe”指的是一个用于检查文件MD5哈希值的软件,版本为1.0.4,该软件的可执行文件名为“Hash_1.0.4_XiaZaiBa.exe”。MD5(Message-Digest Algorithm 5)是一种广泛使用的加密散列函数,产生...

    Hash_1.0.4.exe

    Hash_1.0.4.exe检验文件Hash,

    MD5校验Hash_1.0.4

    MD5校验Hash_1.0.4是一个用于验证软件安全性的工具,它主要通过计算文件的MD5哈希值来确保文件的完整性。MD5(Message-Digest Algorithm 5)是一种广泛使用的加密散列函数,产生一个128位(16字节)的散列值,通常用...

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

    它的语法为 `hash_hmac($algo, $data, $key, $raw_output)`,其中 `$algo` 是哈希算法(如 "sha1"),`$data` 是消息,`$key` 是密钥,`$raw_output` 指示是否返回原始二进制哈希值。`hash_hmac` 能够很好地处理 UTF...

    Hash_1.0.4_0523.zip

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

    hash.rar_HASH算法_fpga hash_hash_zebra85v_哈希表Verilog

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

    HASH_hash_stm32hash_stm32hash表_stm32f407_

    哈希算法能够将任意长度的输入数据转化为固定长度的输出,通常称为哈希值或消息摘要。 哈希算法的主要特性包括: 1. **不可逆性**:无法从哈希值推导出原始数据。 2. **抗碰撞**:对于不同的输入,哈希函数应产生...

    uthash表操作函数

    hash表操作函数 HASH_ADD_INT HASH_ADD_STR

    hash_XE2_by_eGust.zip

    《深入理解Delphi中的哈希算法:以hash_XE2_by_eGust.zip为例》 在编程领域,尤其是在软件开发中,哈希算法扮演着至关重要的角色。它们被广泛用于数据验证、数据索引和密码学等多个方面。本文将重点讨论Delphi编程...

    MD5.c.zip_hash_md5_md5 hash_md5_hash_woodennfx

    这个标题"MD5.c.zip_hash_md5_md5_hash_woodennfx"可能指的是一个包含有关MD5哈希计算源代码的压缩文件,其中可能包含了名为"MD5.c"的C语言源代码文件。 MD5的主要特性是它的单向性,即从任意长度的消息生成固定...

    Hash_1.0.4

    Hash_1.0.4

    hash_md5_sha.rar_SHA_hash_rhhash_文件 hash

    标题“hash_md5_sha.rar_SHA_hash_rhhash_文件 hash”提及了几个关键的哈希算法,包括MD5(Message-Digest Algorithm 5)和SHA(Secure Hash Algorithm),以及一个不太常见的RHHash。描述中提到“文件内容hash算法...

    linux hash_map

    Linux下的`hash_map`是一种基于哈希表的数据结构,它提供了高效的键值对存储功能。与`map`不同,`hash_map`不使用二叉查找树(如红黑树),而是利用哈希函数来实现快速查找。哈希表通常由数组和散列函数组成,数组的...

    Hash_1.0.4_XiaZaiBa

    Hash_1.0.4_XiaZaiBa是一款专为Windows平台设计的轻便哈希计算工具,它能够快速地对文件进行MD5和SHA1哈希值的计算,以验证文件的完整性。 首先,让我们理解什么是哈希。哈希,又称为散列或消息摘要,是一种将任意...

    Hash_1.0.4.zip

    Hash工具,如标题中的"Hash_1.0.4.zip",提供了对文件进行MD5、SHA1以及CRC32等哈希校验的功能,确保了数据在传输、存储过程中的完整性。本文将深入探讨这三种哈希算法及其在实际应用中的重要性。 MD5(Message-...

Global site tag (gtag.js) - Google Analytics