C++ C++;具有整数键的高效紧凑映射

C++ C++;具有整数键的高效紧凑映射,c++,optimization,data-structures,C++,Optimization,Data Structures,我有200组大约50000个在0到500000范围内的唯一整数,我需要映射到另一个小值(一对整数,值是不相关的,因此不需要按需计算) 我尝试使用std::unordered_映射,这使用了大约50MB(在VS2015堆诊断工具中测量),虽然性能良好,但我希望降低内存使用率(打算在一些小型500MB云服务器上作为后台服务) 实际上,我的初始版本是200个独立的std::无序的\u map 一个选项似乎是排序数组并使用二进制搜索,但还有其他选择吗?我认为排序向量应该可以工作,如果排序后不更改向量的话

我有200组大约50000个在0到500000范围内的唯一整数,我需要映射到另一个小值(一对整数,值是不相关的,因此不需要按需计算)

我尝试使用std::unordered_映射,这使用了大约50MB(在VS2015堆诊断工具中测量),虽然性能良好,但我希望降低内存使用率(打算在一些小型500MB云服务器上作为后台服务)

实际上,我的初始版本是200个独立的
std::无序的\u map


一个选项似乎是排序数组并使用二进制搜索,但还有其他选择吗?

我认为排序向量应该可以工作,如果排序后不更改向量的话。它非常节省空间,即没有指针开销


如果您需要更好的性能,请不要介意使用第三方库。您可以尝试使用非常小的空间开销实现哈希映射。

我想最有效的内存将是
std::vector
,就像您已经建议的那样

在这种情况下,由于以下原因,您将只有内存开销:

  • std::vector的固定开销(非常有限)
  • 有时,由于旧数据和新数据必须在“增长”期间处于活动状态,因此在“增长”期间内存使用率会更高
  • std::vector中未使用的空间
您有点指出,在构建之后,您不再需要扩展向量,因此您可以
reserve
shrink\u to\u fit
以除去未使用的空间。(请注意,reserve还修复了增长期间内存使用的峰值)

如果您有更密集的用法,您可以考虑将存储更改为<代码> STD::vector < /代码>或<代码> STD::vector < /代码>。在这种结构中,索引是隐式的,尽管只有当每个索引都有一个值时,内存增益才会显示出来

使用向量的缺点是必须编写一些自定义代码。在这种情况下,
std::unordered_map
std::map
并不是那么糟糕,如果你不介意处理器缓存(L1…)上出现更多的缓存未命中,那么对于不太标准的实现,你可以检查一下,或者,尽管我对此没有任何经验


最后,人们可能会想,为什么这些数据都存储在内存中,尽管如果需要最佳性能,我看不到防止这种情况发生的方法。

为了高效存储,根据精确的值范围,您可能希望使用位操作将键/值对存储在单个值中:例如,如果值非常小,您甚至可以将24位用于键,8位用于值,从而生成单个32位条目。我相信现在大多数编译器使用32位或64位对齐,因此存储例如32位键和16位值可能仍然需要每个条目64位。如果瓶颈是内存总线和缓存未命中,而不是CPU本身,那么使用简单压缩也有利于提高性能

然后,这取决于您希望执行的操作类型。存储键的最简单方法是一个有序的结构数组或我上面提出的ley/value组合条目。这是快速且非常节省空间的,但需要O(logn)查找

如果你想变得更花哨一点,你可以使用,这个想法是找到一个哈希函数,为每个键生成唯一的哈希值。这使得hashmap成为一个简单的数组,只需要稍微大于我上面提出的排序数组。找到一个好的散列函数应该相对比较快,您可以通过使数组稍微大一点并允许数组中有一些未使用的字段来让它变得更容易。 是一个完美散列的实现,但我自己还没有使用过

在这两种情况下,内存消耗都是:(成对数)*(每个条目的位)位,加上使用第二种方法时存储哈希函数

**编辑**


更新自@FireLancer的评论。此外,还添加了一些关于压缩数组性能的词汇。

200个“集合”中的每一个都有自己独特的映射吗?您是否尝试过
std::map
?@Galik在这种情况下既没有节省空间,也没有
std::unordered_map
的性能。我更好奇的是,是否对存储桶大小进行了任何调整。200*50000*4字节的整数等于40兆字节。因此,在50兆字节,包括映射值,我会说你做得很好。假设集合是动态的(因为它们必须是排序的数组工作得很好),我会考虑我在一个更老的答案中概述的技术:我没有看到在第一个例子中的一点操作在这里会有什么帮助。我希望一个
结构值{int x;int y;}
以8个连续字节的形式存储。也许key+value_1+value_2可以是8字节而不是12字节,但需要看看是否可以限制足够的值范围。不过,在运行时构造更好的散列函数的可能性看起来确实很有趣,我们将进行实验,看看它对我的数据集的密度有多大。@FireLancer你是对的,在C/C++中,只有当你不想使用非标准的每个键/值的位宽度(我在Java中考虑过)时,位运算才有帮助。我会更新答案。我不明白
set::set
这东西是怎么工作的。什么东西看起来像什么?至于带有排序数组的自定义代码,我们计划只使用
std::sort
(创建后)和
std::lower_-bound
(查找)。除非您的意思是值和数组索引是键?就像我说的,数据是50000个从0到500000的数字,所以使用这样的数组效率只有10%。另外,在64位平台上,sizeof(unique_ptr)的大小将与2个整数一样大,尽管我认为可以用一个“无效值”代替它们(可能是INT_MAX)。事实上,它代表一些存储,因为我不确定您的表示。(或下一读)