繁体   English   中英

任何更好的替代std :: vector的方法 <std::unique_ptr<T> &gt;?

[英]Any better alternative to std::vector<std::unique_ptr<T>>?

我正在寻找一个需要满足以下要求的容器(针对游戏开发,尤其是实体管理)

  1. 快速迭代
  2. 没有存储元素的副本
  3. 没有使元素的指针无效
  4. 移除和插入元素

例:

Container<Entity> container;

// This pointer will always point to the player
Entity* player{new Entity};          
container.add(player);               

// Set some entities to "dead"
for(auto& e : container) if(e->type == "Enemy") e->die(); 

// Use erase-remove idiom on "dead" entities
container.cleanup();                 

// Player pointer is still valid
player->doSomething();               

到目前为止,我已经尝试了两种不同的容器类型:

  • std::vector<std::unique_ptr<T>>
    1. 缓存友好 (快速迭代)
    2. 没有副本 (感谢std::unique_ptr
    3. 没有指针无效 (感谢std::unique_ptr

...和...

  • std::list<T>
    1. 非缓存友好 (迭代速度较慢)
    2. 无副本
    3. 没有指针无效

即使看起来违反直觉, 根据我的基准std::vector<std::unique_ptr<T>>std::list<T>性能更高。

(对于较大的类型, std::list<T>在插入过程中性能更高,但std::vector<std::unique_ptr<T>>仍然是赢家)


我想知道是否有更好的替代std::vector<std::unique_ptr<T>>

理想地,替代方案应该是高速缓存友好的 ,以实现快速迭代,并且即使在添加/删除现有项目之后,用户也可以引用相同的项目(指针不应无效)

通过性能测试,您正在做正确的事情。 那是回答这个问题的唯一真实方法。

我所知道的唯一可能更快的方法是创建一个缓冲区。 然后为vector<unique_ptr<T>, custom_allocator<unique_ptr<T>>>创建一个自定义分配器,该分配器从缓冲区分配。

还从同一缓冲区分配对象(这样,将unique_ptr的点指向缓冲区)。

为此,您必须知道上限,或者为超出限制时编写溢出逻辑。

使自定义分配器从缓冲区的中间向上增长。

使unique_ptrs的分配从缓冲区的中间向下增加。

只要整个缓冲区都适合高速缓存行,您就会尽可能快。 这并非易事,而且您当前的解决方案可能已经足够好了。

这听起来像是普通的std :: vector是您想要的。

到目前为止,这是最快的迭代,因为您需要对内存的连续部分进行一次扫描。 您只能通过研究T的结构来做得更好。

可以使用O(1)中的push_back插入新元素。 可以通过O(1)中的swap和pop_back的组合来删除单个元素。 可以使用remove_if和擦除组合删除几个元素。

您不能存储指针,因为它们将无效。 但是,您可以做的是存储索引并使向量几乎全局可用。

使用索引代替指针还有两个优点:

  1. ints使用4个字节,而指针使用8个字节。 使用指针会自动导致更大的内存占用,从而导致更多的高速缓存未命中。 我怀疑您将拥有2 ^ 32个以上的实体,所以8个字节是多余的。
  2. 您可以使用数组的结构而不是结构的数组。

为了说明第2点,请看以下两个示例

struct Unit{
    int type, health;
};
vector<Unit>units;
for(int i=0; i<(int)units.size(); ++i)
    if(units[i].type == medic)
        units[i].health += 10;

vector<int>type, health;
for(int i=0; i<(int)type.size(); ++i)
    if(type[i] == medic)
        health[i] += 10;

假设只有很少的医生。 在第一个代码段中,由于类型和运行状况值在内存中相邻,因此必须通过缓存将整个内存通过管道传输。 在第二个示例中,只需要加载所有类型的数据,而医疗人员的健康值很少。 总的来说,需要加载的内存更少,这导致了更快的迭代。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM