用于存储大量物体的高性能容器

时间:2018-01-03 16:49:25

标签: algorithm performance containers

我正在寻找具有以下目标的理想数据容器:

容器的行为必须类似于Queue,具有以下规范:

1)随机访问不是必须的 2)在两个方向上迭代对象必须超快(连续数据会更好)
3)从列表前面执行高性能删除并在后面插入是必须的(每次执行大量删除和附加)
4)项目不是原始类型,它们是对象。

我知道双链表不是高性能容器。 向量(比如c ++中的std :: vector)是好的,但它并没有真正优化从前面删除,我也不认为给定对象大小的矢量化是可能的。

我也在考虑Slot-Map容器​​的可能性,但不确定它是否是最好的选择。

我想知道是否有更好的选择?

2 个答案:

答案 0 :(得分:1)

如果我们真的关心性能,容器不应该动态分配任何内存,即我们应该定义容器中对象的上限。

接口要求确实是排队的,因此看起来最快的选项是指向对象的指针的循环队列。所以容器应该剃掉以下字段:

  1. OBJECT * ptrs[SIZE] - 固定大小的指针数组。当然,我们会在这里浪费SIZE * sizeof (OBJECT *)个字节,但从表现来看,它可能是一个很好的交易。
  2. size_t head_idx - 头对象索引。
  3. size_t tail_idx - 尾对象索引。
  4.   

    在两个方向上迭代对象必须超快

    下一个对象是ptrs[]中的下一个索引:

    if (cur_idx >= head_idx) return nullptr;
    return ptrs[(cur_idx++) % SIZE]; // make sure SIZE is a power of 2 constant
    

    Prev对象是ptrs[]

    中的prev index
    if (cur_idx <= tail_idx) return nullptr;
    return ptrs[(cur_idx--) % SIZE]; // make sure SIZE is a power of 2 constant
    
      

    从列表前面执行高性能删除并在后面插入是必须的

    pop_front()就像:

    一样简单
    if (tail_idx == head_idx) ... // should not happen, through an error
    head_idx++;
    

    push_back()就像:

    一样简单
    if (tail_idx - head_idx >= SIZE) ... // should not happen, through an error
    ptrs[(tail_idx++) % SIZE] = obj_ptr; // make sure SIZE if a power of 2 constant
    
      

    项目不是基本类型,它们是对象

    最通用的解决方案是简单地将指针存储在循环队列中,因此对象的大小无关紧要,而且只浪费SIZE次指针,而不是SIZE次对象。但是可以肯定的是,如果你能负担得起预先分配数千个物体,它应该更快......

    这些是基于您的性能要求的猜测。我不确定你是否有能力为表现换取一些记忆,所以如果不是这样我很抱歉...

答案 1 :(得分:1)

您可以通过常规vectorstart索引来告诉您&#34;真实&#34;数据的开头是。

  • 要附加到后面,请使用常规方法。这有一个摊销的固定时间复杂性,考虑到你会做很多推动,这对你来说可能很好。
  • 从前面删除,增加start
  • 要访问元素i,请使用vector[start + i]
  • 每当你从前面的任何地方删除,或者插入除了后面以外的任何地方时,继续并重新创建整个矢量而没有任何前导删除的条目,并将start重置为零。

优点:

  • 条目位于连续的内存块中
  • 从前面快速删除并(分期付款)快速插入后面
  • 快速随机访问和快速迭代

缺点:

  • 缓慢的最坏情况插入行为
  • 除非定期清理,否则
  • 可能浪费大量空间
  • 清理删除更改删除最坏情况的行为为线性,慢。

无论你做什么,都要考虑与自然方法进行比较:记住头部和尾部的双重链表。

  • 从前/后快速插入/删除
  • 没有浪费的空间

是的,这些项目在内存中不会是连续的,因此可能会有更多的缓存未命中;但是,您可以通过偶尔进行碎片整理来解决这个问题:

  • 为列表中的所有节点分配足够的连续空间
  • 通过遍历链接按顺序重新创建节点
  • 释放原始节点并使用新的节点集作为列表

根据删除/插入/遍历的模式,这可能是可行的。