我正在寻找一个容器(针对游戏开发,尤其是实体管理),需要满足这些要求:
示例:
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>>
std::unique_ptr
)std::unique_ptr
)...和...
std::list<T>
即使看起来违反直觉,std::vector<std::unique_ptr<T>>
的效果也比std::list<T>
according to my benchmarks更高。
(对于较大的类型,std::list<T>
在插入过程中效果更佳,但std::vector<std::unique_ptr<T>>
仍然获胜。
我想知道是否有更好的替代std::vector<std::unique_ptr<T>>
。
理想情况下,替代方法应该是缓存友好型,以便快速迭代,并允许用户在添加/删除现有项之后引用相同的项(指针不应无效) )
答案 0 :(得分:4)
通过性能测试,您正在做正确的事情。这是回答这个问题的唯一真实方式。
我唯一知道的可能更快就是创建一个缓冲区。然后为从缓冲区分配的vector<unique_ptr<T>, custom_allocator<unique_ptr<T>>>
创建自定义分配器。
同样从同一个缓冲区分配对象(以便将unique_ptr指向缓冲区)。
要做到这一点,您必须知道上限,或者在超出限制时写入溢出逻辑。
让自定义分配器从缓冲区中间向上增长。
让unique_ptrs的分配从缓冲区的中间向下增长。
只要整个缓冲区适合缓存行,您就会尽可能快。这并非易事,您当前的解决方案可能已经足够好了。
答案 1 :(得分:1)
这听起来像是一个普通的std :: vector就是你想要的。
这是迄今为止最快的迭代,因为你在内存的连续部分进行一次扫描。你只能通过研究T的结构来做得更好。
可以使用O(1)中的push_back完成插入新元素。删除单个元素可以使用O(1)中的swap和pop_back的组合来完成。删除多个元素可以使用remove_if和erase的组合来完成。
您无法存储指针,因为它们将无效。但是,您可以做的是存储索引并使向量几乎全局可用。
使用索引而不是指针有两个优点:
为了说明第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;
VS
vector<int>type, health;
for(int i=0; i<(int)type.size(); ++i)
if(type[i] == medic)
health[i] += 10;
假设只有极少数的医务人员。在第一个代码片段中,由于类型和健康值在内存中相邻,因此整个内存必须通过缓存传输。在第二个示例中,只需要加载所有类型以及医务人员的少数健康值。总而言之,必须加载更少的内存,这会导致更快的迭代。