使用 uint64 作为 map 的 key, 有哪些优化技巧?

2021-04-19 15:41:33 +08:00
 DinoStray

比如, hash 和 红黑树 哪个更合适.
如果用 hash, 有没有推荐的 hash 算法?

PS: 这个问题我尽量不关联编程语言提问. 实际上我这边是 C++20, 涉及到 std::map, std::unordered_map

1562 次点击
所在节点    问与答
16 条回复
3dwelcome
2021-04-19 15:53:35 +08:00
红黑树不是挺好的,那么经典。比 hash 好,hash 函数有冲突,红黑树是自平衡,没有潜在问题。
minami
2021-04-19 16:22:47 +08:00
一般推荐用 xxhash 或 murmurhash ( 2 或 3 版本),前者比较好,但后者实现简单
geelaw
2021-04-19 17:21:04 +08:00
字典树也可以考虑一下。
geelaw
2021-04-19 17:22:03 +08:00
🤧 如果 key 是连续范围且该范围内稠密,考虑用 vector 。
DinoStray
2021-04-19 17:31:34 +08:00
@geelaw 因为会频繁的增加删除, 所以长时间运行后是不连续的了
3dwelcome
2021-04-19 18:04:44 +08:00
@minami 没有审题啊,楼主 key 是 integer,又不是 string 。你这两个算法都是针对字符串的。

有专门针对 uint64_t 的 hash 函数,你一个都没提到。
3dwelcome
2021-04-19 18:09:37 +08:00
google 搜“Integer Hash Function", 这才是针对整形 key 的散列函数。
3dwelcome
2021-04-19 18:18:31 +08:00
"第 2 条附言 · 55 分钟前
另外我的 key 有个特点, 是一直自增+1 的"

这种特点可以用二分法查找,自己管理一个集合,未必需要依赖于 C++20,只要保持数据始终内存数序排列就可以。
ysc3839
2021-04-19 18:40:11 +08:00
@3dwelcome 这些 hash 函数都是可用于任意二进制数据的,没记错的话 MSVC 的 STL 就是直接用 FNV Hash 去处理整数 key 的。
DinoStray
2021-04-19 18:42:39 +08:00
@3dwelcome 我有个问题, 我看标准库的 hash, 对 int 的处理是直接返回 值, 那么如果我的 uint64 key 一直自增下去, 不是会存在桶一直增加, 内存不够用的情况么? unordered_map 会对桶做收缩么
minami
2021-04-19 19:01:44 +08:00
@3dwelcome 你是认真的吗,我特地去我以前代码里翻出了针对 uint64_t 的 MurmerHash3
struct MurmerHash3
{
inline std::size_t operator()(const uint64_t &key) const noexcept
{
uint64_t x = (key ^ (key >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
x = x ^ (x >> 31);
return x;
}
};
minami
2021-04-19 19:05:51 +08:00
@minami 不好意思,代码里写的 MurmerHash3,实际上查了下是 splitmix64,不知道当年怎么命名的,逃(
DinoStray
2021-04-19 19:13:31 +08:00
@minami 辛苦您看下, 基于我的业务场景, 我这样重定义 hash 有问题么? 因为我的 key 只是简单自增, 不关联任何业务逻辑. 我只是基于避免桶数量过多, 内存溢出, 所以取余了一下, 限制桶数量
iceheart
2021-04-19 20:08:57 +08:00
多频繁?每秒千万次以内查询建议直接 std::map
3dwelcome
2021-04-19 20:39:58 +08:00
@DinoStray "我有个问题, 我看标准库的 hash, 对 int 的处理是直接返回 值, 那么如果我的 uint64 key 一直自增下去, 不是会存在桶一直增加, 内存不够用的情况么?"

刚刚我用 VS2017 调试了一下,和 9 楼的 @ysc3839 同学说的一致,内部对于 uint64_t 都用 FNV-1a hash function 算法处理过一次,不是直接返回的。

既然数列已经打散了,就不用担心 hash 冲突,会内存不够吧。
DinoStray
2021-04-19 20:42:50 +08:00
@3dwelcome g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
我这边用这个做实验.
```
std::unordered_map<uint64_t, uint64_t> map{};
std::cout << "1: " << map.hash_function()(1) << std::endl;
std::cout << "50000: " << map.hash_function()(50000) << std::endl;
```
结果是
1
50000

看来 g++ 的实现, 是直接返回 uint64 值的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/771681

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX