我一直在使用组件实体系统。
很久以前,我想改进它以使用内存池。
阶段1
为简单起见,这是我的古老代码:-
Entity* entity =new Entity();
Com_Physic* comPhys =new Com_Physic();
这是重构的结果:-
Ptr<Entity> entity=centerPool()->createEntity(); //use pool
Ptr<Com_Physic> comPhys=centerPool()->create<Com_Physic>(entity); //use pool
它真的很好用。(每个时间步使用的时间-=10%)
阶段2
现在我有强烈的愿望在任何地方都适应内存池的概念。
但是,在涉及一些数据结构的情况下,这很难。
这是一个示例函数,它返回某个实体的所有(默认)物理体。
它不是真正完整的代码,但足以显示有问题的部分:-
class SystemPhysic : public basicSystem {
//..... other function / field .....
public: MyArray<PhysicObject*> getAllPhysicBody(Ptr<Entity> entity){
Ptr<Com_Physic> comPhysic=entity->get<Com_Physic>();
MyArray<PhysicObject*> allPhy=comPhysic->physicsList;
//^ #problem
return allPhy;
}
};
MyArray<T>
是一个类似于 的自定义数组std::vector
。它使用自己的分配器。
这是它的核心:-
void reserve(int expectedSize){
//......
void* databaseNew=operator new [](expectedSize*sizeof(T));
//......
}
因此,每当我调用MyArray<PhysicObject*>::operator=()
,
... 粗略地说,operator new[]
都会被调用。( #problem
)
记忆再次碎片化。
问题
通用数据结构应该使用内存池概念吗?
如果是这样,如何专业地将内存池概念集成到数据结构中?
我只是想要一个粗略的想法。
完全不包含代码的解决方案是可以接受的。
更多技术信息
有两种类型的问题MyArray<T>
:-
MyArray<T>
作为 Component 的一个字段(例如Com_Physic
)。MyArray<T>
作为从一个系统传递到另一个系统的临时消息。它创建快,删除快。(>70% , 寿命 = 1 个时间步长)
我有一个想法将它分为两种情况,ShortLife_MyArray<T>
& LongLife_MyArray<T>
,
...但我认为我让它太复杂且难以维护。
更糟糕的是,在真正的游戏中,除了 之外,还有很多种数据结构MyArray<T>
,例如MyArray<T1,T2>
、MyMap<T1,T2>
、MySet<T1,&FunctionPointer>
等。
我糟糕的解决方案
到目前为止,这仅显示了我的猜测列表。
解决方案 1(单例,静态)
MyArray<T>
在, MyArray<T1,T2>
,MyMap<T1,T2>
等 中创建一个静态池字段。
所有类型的数据结构都必须在需要时使用该静态池来分配内存。
GlobalDataStructure::staticPool=topLevel()->poolPtr;
//called only once before everything start
坏处:-
- 它是一个静态变量。(意义不大)
- 很难找到应该调用全局设置语句的位置。
- (弱)我只能使用 1 个池。(我不确定这是否有问题。)
mutex
(弱)如果我的游戏是多线程的,我必须使用锁定池。
解决方案2(占上风)
MyArray<T>
在, MyArray<T1,T2>
,MyMap<T1,T2>
等 中创建一个非静态池字段。
每当我想创建一个新的数据结构时,
......我必须通过共享池(来自顶级游戏逻辑)。
MyArray<T> arr=MyArray<T>(topLevel()->poolPtr); //messy
对于不同的情况,可以有许多池。这可能是好的。
当数据结构要分配时,它必须使用该池。
坏处:-
- 我必须为所有类型的数据结构重构所有代码。
- 我必须通过游泳池。代码会更脏一点。
mutex
(弱)如果我的游戏是多线程的, 我必须使用锁定池。
解决方案3(懒惰的方式)
不要做任何事情,实体和组件的池应该足够了。
其他解决方案是过度工程。