<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Hashmap on Tech Explorer</title><link>https://byronhe.com/categories/hashmap/</link><description>C++、Linux、算法、数据结构与密码学的学习笔记与实践记录。</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><copyright>© 2026 BH</copyright><lastBuildDate>Tue, 10 Nov 2020 00:00:00 +0000</lastBuildDate><atom:link href="https://byronhe.com/categories/hashmap/index.xml" rel="self" type="application/rss+xml"/><item><title>开源库 parallel-hashmap 介绍：高性能 线程安全 内存友好的哈希表 和 btree</title><link>https://byronhe.com/post/2020/11/10/parallel-hashmap-btree-fast-multi-thread-intro/</link><pubDate>Tue, 10 Nov 2020 00:00:00 +0000</pubDate><guid>https://byronhe.com/post/2020/11/10/parallel-hashmap-btree-fast-multi-thread-intro/</guid><description>&lt;h2 class="relative group"&gt;1. 背景&#10; &lt;div id="1-背景" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-%e8%83%8c%e6%99%af" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;后台开发很常见一大类需求是 &lt;strong&gt;线程安全&lt;/strong&gt; &lt;strong&gt;高性能&lt;/strong&gt; 容器数据结构&#10;开源的 &lt;a href="https://github.com/greg7mdp/parallel-hashmap" target="_blank" rel="noreferrer"&gt;https://github.com/greg7mdp/parallel-hashmap&lt;/a&gt;&#10;parallel-hashmap 是对 Google 的 abseil-cpp 库的改进，可供开发中直接使用。&lt;/p&gt;</description><content:encoded>&#10;&lt;h2 class="relative group"&gt;1. 背景&#10; &lt;div id="1-背景" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#1-%e8%83%8c%e6%99%af" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;后台开发很常见一大类需求是 &lt;strong&gt;线程安全&lt;/strong&gt; &lt;strong&gt;高性能&lt;/strong&gt; 容器数据结构&#10;开源的 &lt;a href="https://github.com/greg7mdp/parallel-hashmap" target="_blank" rel="noreferrer"&gt;https://github.com/greg7mdp/parallel-hashmap&lt;/a&gt;&#10;parallel-hashmap 是对 Google 的 abseil-cpp 库的改进，可供开发中直接使用。&lt;/p&gt;&#10;&lt;p&gt;参考官网的英文文档，简单翻译介绍如下：&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;[TOC]&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;2. 概览&#10; &lt;div id="2-概览" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#2-%e6%a6%82%e8%a7%88" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;parallel-hashmap 提供了一组卓越的&#10;&lt;strong&gt;hash map&lt;/strong&gt; 实现，&#10;和 可以替代 std::map 和 std::set 的 &lt;strong&gt;btree&lt;/strong&gt; 实现，并有如下特点:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;Header only&lt;/strong&gt;: 无需链接。直接 include 头文件即可用。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;drop-in replacement&lt;/strong&gt; for &lt;code&gt;std::unordered_map&lt;/code&gt;, &lt;code&gt;std::unordered_set&lt;/code&gt;, &lt;code&gt;std::map&lt;/code&gt; and &lt;code&gt;std::set&lt;/code&gt;。 原样替换，api 完全兼容标准 stl 容器 。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;要求支持 &lt;strong&gt;C++11&lt;/strong&gt; 的编译器 , 并且&lt;strong&gt;提供 C++14 和 C++17 的 API (例如 &lt;code&gt;try_emplace&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;Very efficient&lt;/strong&gt;, 明显比编译器默认提供的 unordered map/set 快， 也比 boost 的实现快，比 &lt;a href="https://github.com/greg7mdp/sparsepp" target="_blank" rel="noreferrer"&gt;sparsepp&lt;/a&gt; 快。这里有个 benchmark ：&#10;&lt;a href="https://martin.ankerl.com/2019/04/01/hashmap-benchmarks-01-overview/" target="_blank" rel="noreferrer"&gt;https://martin.ankerl.com/2019/04/01/hashmap-benchmarks-01-overview/&lt;/a&gt; ，大概 比 std::unorder_map 就是 insert快1倍，find 快3倍&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;Memory friendly&lt;/strong&gt;: 低内存占用，尽管略微高于 &lt;a href="https://github.com/greg7mdp/sparsepp" target="_blank" rel="noreferrer"&gt;sparsepp&lt;/a&gt; 。&#10;参考 abseil 的数据，https://abseil.io/about/design/btree&#10;目前 64位模式下， libstdc++ 实现的 std::set&amp;lt;int32_t&amp;gt; 对插入的每个value，分配 40 字节， 而 absl::btree_set&amp;lt;int32_t&amp;gt; 对每个 value 分配 ~4.3 至 ~5.1 字节&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;支持 &lt;strong&gt;heterogeneous lookup&lt;/strong&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;很容易前向声明 &lt;strong&gt;forward declare&lt;/strong&gt;: 只需要在头文件中 include &lt;code&gt;phmap_fwd_decl.h&lt;/code&gt; 来声明 Parallel Hashmap 容器就行 [注: 目前这种对包含指针作为 key 的哈希表不适用。]&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;Dump/load&lt;/strong&gt; 特性: 当一个 &lt;code&gt;flat&lt;/code&gt; 哈希表存储了 &lt;code&gt;std::trivially_copyable&lt;/code&gt; 的数据时, 表可以被 dump 到磁盘文件，并作为一个简单的数组高效地 restore 恢复，并且过程中不需要进行 hash 计算。这通常比逐个元素地序列化到磁盘快 10倍，但是会额外占用 10% - 60% 的磁盘空间。 见 &lt;code&gt;examples/serialize.cc&lt;/code&gt;. &lt;em&gt;(flat hash map/set only)&lt;/em&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;Tested&lt;/strong&gt; 在这些平台上经过了测试： Windows (vs2015 &amp;amp; vs2017, vs2019, Intel compiler 18 and 19), linux (g++ 4.8.4, 5, 6, 7, 8, clang++ 3.9, 4.0, 5.0) 和 MacOS (g++ and clang++) - click on travis and appveyor icons above for detailed test status.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;自动支持 &lt;strong&gt;boost 的 hash_value()&lt;/strong&gt; 方法，用来提供哈希函数 (见 &lt;code&gt;examples/hash_value.h&lt;/code&gt;). 并且提供 &lt;code&gt;std::pair&lt;/code&gt; 和 &lt;code&gt;std::tuple&lt;/code&gt; 的哈希函数实现.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;strong&gt;natvis&lt;/strong&gt; visualization support in Visual Studio &lt;em&gt;(hash map/set only)&lt;/em&gt;&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 class="relative group"&gt;3. 快速 和 内存友好&#10; &lt;div id="3-快速-和--内存友好" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#3-%e5%bf%ab%e9%80%9f-%e5%92%8c--%e5%86%85%e5%ad%98%e5%8f%8b%e5%a5%bd" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;作者有个介绍文章 &lt;a href="https://greg7mdp.github.io/parallel-hashmap/" target="_blank" rel="noreferrer"&gt;讲解了 Parallel Hashmap 的设计和好处&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;总结起来说，Google absl 的 flat_hash_map 由于采用 2倍的内存增长 resize 策略，所以有个内存占用的尖峰 peak， 在尖峰时刻，需要3倍的临时内存占用，来把旧的 bucket 里面的数据移动到 新的2倍大的 bucket 里面。&lt;/p&gt;&#10;&lt;p&gt;文章提出的想法就是，把一个大的 flat_hash_map 拆分成2的倍数个比如 16个，这样内存峰值就只有原来的 1/16 了。&lt;/p&gt;&#10;&lt;p&gt;并且这种拆分还便于进行并发。&lt;/p&gt;&#10;&lt;p&gt;本库提供的 hashmap 和 btree 基于 Google 在 Abseil 库中开源的实现。 hashmap 使用了 closed hashing，就是 value 直接存成一个内存数组，避免内存间接访问。通过使用并发的 SSE2 指令，这些 hashmap 可以允许一次并发查找 16 个槽位，即使当 hashmap 的填充率已经达到 87.5% ，也可以保持快速。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;重要:&lt;/strong&gt; 本仓库借鉴了 &lt;a href="https://github.com/abseil/abseil-cpp" target="_blank" rel="noreferrer"&gt;abseil-cpp&lt;/a&gt; 仓库的代码, 做了修改，并且可能和原版本行为不同。本仓库是独立工作，像其他开源项目一样不提供保证。请访问 &lt;a href="https://github.com/abseil/abseil-cpp" target="_blank" rel="noreferrer"&gt;abseil-cpp&lt;/a&gt; 获取官方的 Abseil 库。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;4. 安装&#10; &lt;div id="4-安装" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#4-%e5%ae%89%e8%a3%85" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;无需安装，代码里面直接 include 就行。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;5. 例子&#10; &lt;div id="5-例子" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#5-%e4%be%8b%e5%ad%90" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;parallel_hashmap/phmap.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;phmap&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;flat_hash_map&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Create an unordered_map of three strings (that map to strings)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;flat_hash_map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;tom&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;tom@gmail.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;jeff&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;jk@gmail.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;jim&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;jimg@microsoft.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Iterate and print keys and values &#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nl"&gt;n&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&amp;#39;s email is: &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Add a new entry&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;bill&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;bg@whatever.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// and print it&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;bill&amp;#39;s email is: &amp;#34;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;bill&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&#10;&lt;h2 class="relative group"&gt;6. 提供的各种哈希表及其优缺点&#10; &lt;div id="6-提供的各种哈希表及其优缺点" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#6-%e6%8f%90%e4%be%9b%e7%9a%84%e5%90%84%e7%a7%8d%e5%93%88%e5%b8%8c%e8%a1%a8%e5%8f%8a%e5%85%b6%e4%bc%98%e7%bc%ba%e7%82%b9" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;头文件 &lt;code&gt;parallel_hashmap/phmap.h&lt;/code&gt; 提供下列 8 种 哈希表 :&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;phmap::flat_hash_set&lt;/li&gt;&#10;&lt;li&gt;phmap::flat_hash_map&lt;/li&gt;&#10;&lt;li&gt;phmap::node_hash_set&lt;/li&gt;&#10;&lt;li&gt;phmap::node_hash_map&lt;/li&gt;&#10;&lt;li&gt;phmap::parallel_flat_hash_set&lt;/li&gt;&#10;&lt;li&gt;phmap::parallel_flat_hash_map&lt;/li&gt;&#10;&lt;li&gt;phmap::parallel_node_hash_set&lt;/li&gt;&#10;&lt;li&gt;phmap::parallel_node_hash_map&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;头文件 &lt;code&gt;parallel_hashmap/btree.h&lt;/code&gt; 提供一下基于 btree 的有序容器 ordered containers:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;phmap::btree_set&lt;/li&gt;&#10;&lt;li&gt;phmap::btree_map&lt;/li&gt;&#10;&lt;li&gt;phmap::btree_multiset&lt;/li&gt;&#10;&lt;li&gt;phmap::btree_multimap&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;btree 容器是直接移植了 Abseil，因此应该是 Abseil 的表现一样，除了细微不同（例如支持 std::string_view 而不是 absl::string_view，并且有前向声明）&lt;/p&gt;&#10;&lt;p&gt;当 btree 被修改，value 可能在内存中被移动。这意味着当 btree 容器被修改时，指向 btree 容器中的 value 的指针或者迭代器会失效。 这和 &lt;code&gt;std::map&lt;/code&gt; / &lt;code&gt;std::set&lt;/code&gt; 显著不同, std 容器提供指针稳定性的保证。 &amp;lsquo;flat&amp;rsquo; hash map 和 set 也会有这种失效。&lt;/p&gt;&#10;&lt;p&gt;全部的类型和模板参数可以在这个头文件看到：&#10;&lt;a href="https://raw.githubusercontent.com/greg7mdp/parallel-hashmap/master/parallel_hashmap/phmap_fwd_decl.h" target="_blank" rel="noreferrer"&gt;parallel_hashmap/phmap_fwd_decl.h&lt;/a&gt; , 这个头文件也可以用于前向声明 Parallel Hashmap 库。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;哈希容器的关键设计点&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;名字包含 &lt;code&gt;flat&lt;/code&gt; 的哈希表内部会移动 key 和 value，所以如果在外部包含了指向 &lt;code&gt;flat&lt;/code&gt; 哈希表中元素的指针，当哈希表被修改时，指针可能会变成野指针。而名字包含 &lt;code&gt;node&lt;/code&gt; 的哈希表内部不会移动 key 和 value，可以用在这种场合。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;code&gt;flat&lt;/code&gt; 系列哈希表内存占用更少，并且通常比 &lt;code&gt;node&lt;/code&gt; 系列哈希表更快，所以尽可能使用 &lt;code&gt;flat&lt;/code&gt;系列。当然，例外情况是 value 很大（比如大于 100字节），在内存中移动时很慢的话，此时应使用 &lt;code&gt;node&lt;/code&gt; 系列。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;code&gt;parallel&lt;/code&gt; 系列的哈希表。当你需要在少数个哈希表中存储非常大量的 value 时， 应该优先用 &lt;code&gt;parallel&lt;/code&gt; 系列 哈希表。而如果你需要在大量 哈希表中，每个存储相对少量的元素时，优先用非 &lt;code&gt;parallel&lt;/code&gt;系列的哈希表。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;code&gt;parallel&lt;/code&gt; 系列哈希表的好处是:&lt;br&gt;&#10;a. 减少了 resize 时候内存占用的峰值。&lt;br&gt;&#10;b. 可以打开多线程支持 (由于内部拆分成多个子哈希表，所以可以借此实现并发安全。)&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;strong&gt;btree 系列容器中的关键设计&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Btree 系列容器是有序容器，可以作为&#10;&lt;code&gt;std::map&lt;/code&gt; 和 &lt;code&gt;std::set&lt;/code&gt; 的替代。他们在每个树节点中存储多个value，因此更缓存友好，并且内存占用显著更低。&lt;/p&gt;&#10;&lt;p&gt;通常都应该优先用 Btree 容器，来代替 STL 中默认的红黑树容器。也有例外：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;要求指针稳定性，或者迭代器稳定性。&lt;/li&gt;&#10;&lt;li&gt;value 类型巨大，在内存中移动起来比较昂贵。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;当不需要顺序的时候， 通常哈希表容器是比 btree容器 更好的选择。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;7. 对 Abseil&amp;rsquo;s 哈希表的改动&#10; &lt;div id="7-对-abseils-哈希表的改动" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#7-%e5%af%b9-abseils-%e5%93%88%e5%b8%8c%e8%a1%a8%e7%9a%84%e6%94%b9%e5%8a%a8" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;默认哈希，从 absl::hash 改成了 std::hash。 可以通过定义宏&lt;code&gt;PHMAP_USE_ABSL_HASH&lt;/code&gt;改变。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;code&gt;erase(iterator)&lt;/code&gt; 和 &lt;code&gt;erase(const_iterator)&lt;/code&gt; 都返回指向被删除的元素的下一个元素的迭代器，和 std::unordered_map 一样. 并提供了一个非标准的 &lt;code&gt;void _erase(iterator)&lt;/code&gt; ，用于不需要返回 value 的场合。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;没有提供 &lt;code&gt;absl::string_view&lt;/code&gt; 这种新类型。 &lt;code&gt;std::hash&amp;lt;&amp;gt;&lt;/code&gt; 支持的所有类型 phmap 都支持 (如果编译器提供了 &lt;code&gt;std::string_view&lt;/code&gt; 那 phmap 当然也支持).&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;Abseil 哈希表内部会随机初始化一个哈希种子，这样迭代顺序就是非确定性的，当哈希表被用于面向用户的 web 服务场合的时候，这可以用于阻止 &lt;em&gt;Denial Of Service&lt;/em&gt; 攻击，但是这使得调试变困难了。 &lt;em&gt;phmap&lt;/em&gt; 哈希表默认不会实现这种随机初始化，但可以通过在 include &lt;code&gt;phmap.h&lt;/code&gt; 之前 定义宏 &lt;code&gt;#define PHMAP_NON_DETERMINISTIC 1&lt;/code&gt; 来打开这种随机初始化 (参考 raw_hash_set_test.cc).&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;和 Abseil 哈希表不一样, 我们内部做了 哈希值的混合。这在用户提供的哈希函数熵分布比较差的时候， 可以避免哈希表出现严重性能下降。这代理的性能开销是非常低的，并且在使用 &lt;em&gt;不完美&lt;/em&gt; 的哈希函数的时候， 也能提供稳定的性能。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 class="relative group"&gt;8. 内存使用&#10; &lt;div id="8-内存使用" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#8-%e5%86%85%e5%ad%98%e4%bd%bf%e7%94%a8" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;type&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;memory usage&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;additional &lt;em&gt;peak&lt;/em&gt; memory usage when resizing&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;flat tables&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;figure&gt;&#10; &lt;img class="my-0 rounded-md" loading="lazy" decoding="async" alt="flat_mem_usage" src="https://github.com/greg7mdp/parallel-hashmap/blob/master/html/img/flat_mem_usage.gif?raw=true"&gt;&lt;/figure&gt;&#10;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;figure&gt;&#10; &lt;img class="my-0 rounded-md" loading="lazy" decoding="async" alt="flat_peak_usage" src="https://github.com/greg7mdp/parallel-hashmap/blob/master/html/img/flat_peak.gif?raw=true"&gt;&lt;/figure&gt;&#10;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;node tables&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;figure&gt;&#10; &lt;img class="my-0 rounded-md" loading="lazy" decoding="async" alt="node_mem_usage" src="https://github.com/greg7mdp/parallel-hashmap/blob/master/html/img/node_mem_usage.gif?raw=true"&gt;&lt;/figure&gt;&#10;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;figure&gt;&#10; &lt;img class="my-0 rounded-md" loading="lazy" decoding="async" alt="node_peak_usage" src="https://github.com/greg7mdp/parallel-hashmap/blob/master/html/img/node_peak.gif?raw=true"&gt;&lt;/figure&gt;&#10;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;parallel flat tables&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;figure&gt;&#10; &lt;img class="my-0 rounded-md" loading="lazy" decoding="async" alt="flat_mem_usage" src="https://github.com/greg7mdp/parallel-hashmap/blob/master/html/img/flat_mem_usage.gif?raw=true"&gt;&lt;/figure&gt;&#10;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;figure&gt;&#10; &lt;img class="my-0 rounded-md" loading="lazy" decoding="async" alt="parallel_flat_peak" src="https://github.com/greg7mdp/parallel-hashmap/blob/master/html/img/parallel_flat_peak.gif?raw=true"&gt;&lt;/figure&gt;&#10;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;parallel node tables&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;figure&gt;&#10; &lt;img class="my-0 rounded-md" loading="lazy" decoding="async" alt="node_mem_usage" src="https://github.com/greg7mdp/parallel-hashmap/blob/master/html/img/node_mem_usage.gif?raw=true"&gt;&lt;/figure&gt;&#10;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;figure&gt;&#10; &lt;img class="my-0 rounded-md" loading="lazy" decoding="async" alt="parallel_node_peak" src="https://github.com/greg7mdp/parallel-hashmap/blob/master/html/img/parallel_node_peak.gif?raw=true"&gt;&lt;/figure&gt;&#10;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;em&gt;size()&lt;/em&gt; 是容器中的 value 数量，通过 size() 方法返回。&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;load_factor()&lt;/em&gt; 是比例: &lt;code&gt;size() / bucket_count()&lt;/code&gt;. 从 0.4375 (刚 resize 之后) 到 0.875 (在 resize 之前) 之间波动. bucket 数组的大小每次 resize 都 double。&lt;/li&gt;&#10;&lt;li&gt;value 9 来自 &lt;code&gt;sizeof(void *) + 1&lt;/code&gt;, 由于对 bucket 数组 中的每一个 entry， &lt;em&gt;node&lt;/em&gt; 哈希表存储一个指针加一个字节的元数据，&lt;/li&gt;&#10;&lt;li&gt;flat 哈希表存储 value， 加每个value 一个字节的元数据，直接存储在 bucket 数组中，因此得到 &lt;code&gt;sizeof(C::value_type) + 1&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;resize 时候的额外峰值内存占用，取决于旧的 bucket 数组的大小（是新bucket数组大小的 0.5倍），就是需要被复制到新bucket数组的 value，并在复制完成后，老的 value 会被释放。&lt;/li&gt;&#10;&lt;li&gt;&lt;em&gt;parallel&lt;/em&gt; 哈希表,当用模板参数 N=4 时, 创建 16 个 submap. 当哈希值均匀分布，并在单线程模式下, 任何时间点都只有一个 submap 做 resize，因此比值约等于 &lt;code&gt;0.03&lt;/code&gt; 即 &lt;code&gt;0.5 / 16&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 class="relative group"&gt;9. 哈希表容器的 Iterator 迭代器失效&#10; &lt;div id="9-哈希表容器的-iterator-迭代器失效" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#9-%e5%93%88%e5%b8%8c%e8%a1%a8%e5%ae%b9%e5%99%a8%e7%9a%84-iterator-%e8%bf%ad%e4%bb%a3%e5%99%a8%e5%a4%b1%e6%95%88" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;规则和 &lt;code&gt;std::unordered_map&lt;/code&gt; 相同。&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Operations&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Invalidated&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;All read only operations, swap, std::swap&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Never&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;clear, rehash, reserve, operator=&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Always&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;insert, emplace, emplace_hint, operator[]&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Only if rehash triggered&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;erase&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Only to the element erased&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&#10;&lt;h2 class="relative group"&gt;10. btree 容器的 Iterator 失效&#10; &lt;div id="10-btree-容器的-iterator-失效" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#10-btree-%e5%ae%b9%e5%99%a8%e7%9a%84-iterator-%e5%a4%b1%e6%95%88" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;不同于 &lt;code&gt;std::map&lt;/code&gt; 和 &lt;code&gt;std::set&lt;/code&gt;, 任何修改操作都可能使得存活的迭代器失效。&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Operations&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Invalidated&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;All read only operations, swap, std::swap&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Never&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;clear, operator=&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Always&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;insert, emplace, emplace_hint, operator[]&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Yes&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;erase&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Yes&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&#10;&lt;h2 class="relative group"&gt;11. Example 2 - 为用户自定义类提供 hash 函数&#10; &lt;div id="11-example-2---为用户自定义类提供-hash-函数" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#11-example-2---%e4%b8%ba%e7%94%a8%e6%88%b7%e8%87%aa%e5%ae%9a%e4%b9%89%e7%b1%bb%e6%8f%90%e4%be%9b-hash-%e5%87%bd%e6%95%b0" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;为了使用 flat_hash_set 或者 flat_hash_map，自定义类需要提供一个 哈希函数。可以通过以下2种方法之一实现：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;通过 HashFcn 模板参数提供一个 hash 函数&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;使用 boost 的话，可以在自定义类中加一个 &lt;code&gt;hash_value()&lt;/code&gt; friend 函数.&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;例子自己看官方文档吧， 就不贴了。&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;12. 线程安全性&#10; &lt;div id="12-线程安全性" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#12-%e7%ba%bf%e7%a8%8b%e5%ae%89%e5%85%a8%e6%80%a7" aria-label="锚点"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Parallel Hashmap 容器遵循 C++ 标准库的线程安全规则。具体地:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;单个 phmap 哈希表从多个线程读，是线程安全的。例如，给定一个哈希表 A，从 thread 1 和 thread 2 并发读是安全的。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;如果单个哈希表在被一个线程写，在任何线程进行的，对该哈希表的读写操作，都是不安全的，需要被保护。例如，给定哈希表 A， 如果 thread 1 在写 A， 比如避免 thread 2 在同时读或者写 A。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;不同线程对同一种 type 的不同实例，并发进行读写，是安全的。例如，给定相同类型的哈希表 A 和 B , 在 thread 1 中写 A， 并且在 thread 2 中读 B ，是安全的。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;&lt;em&gt;parallel&lt;/em&gt; 系列的哈希表，可以通过提供一个 synchronization type (例如 std::shared_mutex, boost::shared_mutex, &lt;a href="https://en.cppreference.com/w/cpp/thread/mutex" target="_blank" rel="noreferrer"&gt;std::mutex&lt;/a&gt;, ) 作为模板的最后一个参数， 变成读写操作内部线程安全的。因为内部是在 &lt;em&gt;submap&lt;/em&gt; 子哈希表上进行了加锁，可以获得一种较大的并发水平。读操作可以通过 &lt;code&gt;if_contains()&lt;/code&gt; 安全地进行, 通过持有 &lt;em&gt;submap&lt;/em&gt; 的锁，并把 value 的引用传给回调函数。类似地， 用 &lt;code&gt;modify_if&lt;/code&gt; 或 &lt;code&gt;try_emplace_l&lt;/code&gt;可以进行安全的写操作。但是要注意，标准 API 返回的迭代器或者引用并没有被 mutex 保护，所以当另一个线程可能修改哈希表时，不能可靠地使用他们。&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&#10;&lt;p&gt;如果使用各种 mutex 类型的例子，包括 boost::mutex, boost::shared_mutex 和 absl::Mutex 可以参考 &lt;code&gt;examples/bench.cc&lt;/code&gt;&#10;(推荐使用 C++17 的 std::shared_mutex ，因为实测性能最好)&lt;/p&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;parallel_系列容器的线程安全的函数有：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;insert()&lt;/li&gt;&#10;&lt;li&gt;emplace()&lt;/li&gt;&#10;&lt;li&gt;lazy_emplace()&lt;/li&gt;&#10;&lt;li&gt;erase()&lt;/li&gt;&#10;&lt;li&gt;merge()&lt;/li&gt;&#10;&lt;li&gt;swap()&lt;/li&gt;&#10;&lt;li&gt;rehash()&lt;/li&gt;&#10;&lt;li&gt;find()&lt;/li&gt;&#10;&lt;li&gt;bucket_count()&lt;/li&gt;&#10;&lt;li&gt;has_element()&lt;/li&gt;&#10;&lt;li&gt;if_contains()&lt;/li&gt;&#10;&lt;li&gt;modify_if()&lt;/li&gt;&#10;&lt;li&gt;try_emplace_l()&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;多线程的例子，例如&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;assert.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;map&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;shared_mutex&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;thread&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;unordered_map&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;vector&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;#34;parallel-hashmap/parallel_hashmap/phmap.h&amp;#34;&lt;/span&gt;&lt;span class="cp"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 替代：std::unordered_map&amp;lt;uint64_t, uint32_t&amp;gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;typedef&lt;/span&gt; &lt;span class="n"&gt;phmap&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;parallel_flat_hash_map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;phmap&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;priv&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hash_default_hash&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;phmap&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;priv&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hash_default_eq&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;allocator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_mutex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MapT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MapT&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;key_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;thread_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;key_num&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;try_emplace_l&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kr"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;thread_list&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;thread_num&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;thread_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="kr"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;]()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;key_num&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;modify_if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[](&lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}));&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nl"&gt;t&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;thread_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt; &lt;span class="n"&gt;hit_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;key_num&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;if_contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thread_num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;hit_num&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hit_num&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;key_num&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;我的博客即将同步至腾讯云+社区，邀请大家一同入驻：(&lt;a href="https://cloud.tencent.com/developer/support-plan?invite_code=3qw4g1gk0xgkc" target="_blank" rel="noreferrer"&gt;https://cloud.tencent.com/developer/support-plan?invite_code=3qw4g1gk0xgkc&lt;/a&gt;)&lt;/p&gt;&#10;</content:encoded></item></channel></rss>