C++ 不同数据结构的速度/内存使用估计

C++ 不同数据结构的速度/内存使用估计,c++,data-structures,hashtable,binary-tree,radix-tree,C++,Data Structures,Hashtable,Binary Tree,Radix Tree,我正试图决定以下内容使用哪种数据结构 假设我有1000万个键,其中包含指向包含一些数据的唯一对象的指针 这些键是UUID,我们可以将它们看作16字节二进制数组。UUID是使用高质量随机数生成器生成的 我一直在考虑以下问题,但想知道在速度和内存消耗方面,每种方法的优缺点是什么。一些公平的估计,64位平台上的最佳/最差/平均情况会更好 我需要能够有几乎无限的项目插入 二叉树 哈希表 基数树(基于位或2位多路) 我需要的操作有:插入、删除、搜索 我喜欢基数树的想法,但它被证明是最难实现的,我还没有找到

我正试图决定以下内容使用哪种数据结构

假设我有1000万个键,其中包含指向包含一些数据的唯一对象的指针

这些键是UUID,我们可以将它们看作16字节二进制数组。UUID是使用高质量随机数生成器生成的

我一直在考虑以下问题,但想知道在速度和内存消耗方面,每种方法的优缺点是什么。一些公平的估计,64位平台上的最佳/最差/平均情况会更好

我需要能够有几乎无限的项目插入

二叉树 哈希表 基数树(基于位或2位多路)

我需要的操作有:插入、删除、搜索


我喜欢基数树的想法,但它被证明是最难实现的,我还没有找到一个合适的实现,我可以将其整合到商业产品中。

我会先尝试
std::map
std::unordered\u map

多年来,他们有许多聪明人在发展和改进他们


有什么原因不能使用
std::map
std::unordered\u map

IMO基数树不难实现。然而,一个简单的哈希表就足够了。只需分配由2^16个对象列表组成的数组,并使用UUID的前2个字节索引要插入对象的列表。然后,您可以搜索包含大约160个项目的列表


或者,分配20M指针数组。要存储对象,只需在0-20M范围内对UUID进行散列,找到第一个空闲(NULL)指针并将其存储在那里。搜索意味着从散列值走到第一个空值。删除也很简单。。。。试着读一读

我刚刚做了一个快速的计算,我想你可能对标准树没问题。1000万把钥匙是一个合理的数字。用一个平衡树,将一个只有23个节点的深度进行检查。对于基数树,实际上需要检查128位的密钥长度

您的密钥也可以非常便宜地进行表示和比较。使用两个64位值的元组(boost或0x)获得相同的128位密钥。元组顺序足以在映射中使用。因此,密钥复制和比较一样便宜。对于基数深度搜索,按原样比较整数可能比进行掩蔽和基于位的比较便宜

因此,在这种情况下,地图很可能工作得很好

*我在这里避免使用
无序的
,因为uuid往往是结构化数据。这意味着一个标准的散列过程(对于散列映射)的性能可能很差*

更新:

因为您使用的是随机UUID,所以散列可能很好——尽管如此大的散列表有很大的内存开销来保持效率

此外,给定完全随机的UUID,基数可能最终与树具有相同的平衡(因为密钥分布是完全均匀的)。因此,您可能无法保存偶数步数,并且仍然会产生位操作的开销。但是有很多方法可以专门化和优化基数树,所以很难说它是更快,还是总是更慢。

  • 你不在乎点菜吗
  • 你的钥匙已经是随机的了
  • 1000万件
简短的回答

哈希表可能最适合您的情况

速度

如果散列是常量,则散列表(
std::unordered_map
)将为O(1)。在您的例子中,O(1)保持不变,因为您甚至不需要使用随机UUID的较低32位进行散列,这应该足够好了。查找的成本类似于一个或两个指针间接寻址

二叉树(
std::map
)将是O(log2n),因此对于1000万个项目,您将有24个比较和24个潜在缓存未命中。即使对于n=4000,它也将使用12个比较,因此它很快就会变得比哈希表更糟糕

基数树是O(k),因此最多有k个比较和k个潜在缓存未命中。在极不可能的最佳情况下,基数树将与哈希表一样快。更糟糕的是(假设k=一个合理的16,对于256路树),它的性能比二叉树好,但比哈希表差得多

所以,如果速度是最高优先级,那么使用哈希表

开销

一个典型的哈希表如果已满,则每个项目大约有1–3个开销指针。如果未满,则可能会在每个空插槽中浪费1个指针的空间。你应该能够保持它几乎满的同时仍然比二叉树快,因为你有一个非常随机的键,但是为了最大可能的速度,你当然要给它足够的空间。对于32位机器上的1000万项,整个表的开销预计为38–114MiB。对于半满的表,预期为76–153MiB

红黑树是最常见的
std::map
实现,每个项有3个指针+1个布尔。一些实现利用指针对齐将bool与其中一个指针合并。根据实现和哈希表的完整程度,红黑树的开销可能略低。预计114-153MiB

基数树的每个项有一个指针,每个空槽有一个指针。不幸的是,我认为这样大的随机键会导致在树的边缘有很多空槽,所以它可能会比上面任何一个使用更多的内存。降低k可以降低此开销,但同样会降低性能

如果最小开销很重要,请使用哈希表或二叉树。如果是优先级,请使用完整的哈希表

请注意,
std::unordered_map
不允许您控制它何时调整大小,因此很难获得一个完整的映射。有一个非常好的
无序映射
实现,可以让您直接控制