C++与整数密钥

问题描述:

我有200套约50,000唯一整数0至500,000我需要映射到另一个小的值的范围的高效且紧凑的地图(整数的一对,的值是不相关的,从而没有点播计算)。C++与整数密钥

我试过使用std :: unordered_maps,并使用了大约50MB(在VS2015堆诊断工具中测量),而性能很好Id想要得到这种内存使用下来(打算成为一些小的后台服务500MB云服务器)。

切实我最初的版本是200独立std::unordered_map<int, std::pair<int, int>>

一个选项似乎是一个排序数组,并使用二分搜索,但还有什么?

+1

200个“套”中的每一套都有自己独特的地图吗? – WhozCraig

+0

你尝试过'std :: map'吗? – Galik

+0

@Galik对于这种情况既不占用空间,也不像性能,特别是'std :: unordered_map'。我更加好奇是否有任何调整桶的大小。 – WhozCraig

我认为排序的向量应该工作,如果你不会改变它的排序向量。它非常节省空间,即无指针开销。

如果您需要更好的性能,并且不介意一些第三方库。你可以尝试sparse_hash_map,它实现了非常小的空间开销哈希映射。

我想这是最有效的记忆将是一个std::vector<std::pair<int, std::set<Something>>>,就像你已经建议。

在这种情况下,你只会有内存开销的结果:

  • 从性病::向量(非常有限)
  • 有时在“成长”更高的内存使用的固定开销作为旧数据和新的必须是活在那一刻
  • 性病未使用的空间:: vector的

你还挺表明,后集结你不再延长载体,所以要么你可以reserveshrink_to_fit摆脱未使用的空间。 (请注意,储备还修复了增长期间内存使用率的上涨)

如果您的密码使用率更高,则可以考虑将存储更改为std::vector<std::set<Something>>std::vector<std::unique_ptr<std::set<Something>>>。在这种结构中,索引是隐含的,但只有在每个索引都有值时才会显示内存增益。

使用矢量的缺点是你必须编写一些自定义代码。在这种情况下,std::unordered_mapstd::map并不坏,如果你不介意少的标准实现处理器缓存(L1 ...)更多高速缓存未命中,一个可以检查出Googles sparsehashGoogles cpp-btreeFacebooks AtomicHashMap from Folly,虽然我不没有任何经验。

最后,我们可以知道为什么你都在存储器中的数据,但我没有看到一个方法来防止这种情况,如果你需要的最佳性能。

+0

我不明白'set :: set'是如何工作的。 “Something”看起来像什么?至于具有排序数组的自定义代码,计划只使用'std :: sort'(创建后)和'std :: lower_bound'(查找)。 –

+0

除非你的意思是什么是值和数组索引是关键?就像我说的那样,数据是从0到500,000的50,000个数字,所以使用这样的数组只有大约10%的效率。在64位平台上,sizeof(unique_ptr)的大小也只有2个整数,尽管我认为我可以为它们设置一个“无效值”(可能是INT_MAX)。 –

+0

的确,它代表着一些存储空间,因为我不确定你的表示。 (或者这个线程的下一个读者) – JVApen

高效的存储,这取决于精确值范围内,您可能需要使用位操作的键/值对存储在一个单一的值:例如,如果值是非常小的,你甚至可以使用24位的键和8位的值,导致一个单一的32位条目。我相信现在大多数编译器现在都使用32位或64位对齐方式,因此存储例如32位密钥和16位值可能仍然需要每个条目64位。如果瓶颈是内存总线和高速缓存未命中,使用简单压缩对性能也有好处,而不是CPU本身。

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

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

在这两种情况下,内存消耗将为:(对数)*(每个条目的位数)位,以及在使用第二种方法时存储散列函数。

**编辑**

从@FireLancer评论后更新。另外,增加了一些关于压缩数组性能的文字。

+0

我没看到你的第一个例子中的一点操作在这里会有帮助。我期望一个'struct Value {int x; int y; }'无论如何都要存储为8个连续字节。也许key + value_1 + value_2可以说是8个字节而不是12个,但是需要看看是否可以限制足够的值范围。在运行时构建一个更好的散列函数的可能性看起来很有趣,但是会试验看看它与我的数据集有多密集。 –

+0

@FireLancer你说得对,在C/C++中,如果你不想使用每个键/值的非标准位宽(我在Java中考虑),那么位操作只会有所帮助。我会更新答案。 – TilmannZ