最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
对象池加速游戏内存分配的解决方案
时间:2022-06-25 04:52:19 编辑:袖梨 来源:一聚教程网
相比内存池,对象池更易用更容易管理,而且还可以利用脏数据,也就是上次被回收掉的对象的数据。而且偶尔的空间分配失败其实不是那么重要(后面会讲怎么在会失败的情况下完成分配任务),游戏中还是速度更重要些。
原理
一次申请大量连续内存(整数个对象大小),最好用堆,当然如果用栈数组也没人拦你,栈空间可是相当有限…
由于分配的对象生存期是不固定的(如下图),池不可能保持已分配对象的连续性,这时进行块移动会降低程序效率。
分配 | 分配 | 分配 | 分配 |
所以需要把闲置对象的指针放入容器中来管理。此容器必须能快速存取删,而且不需要频繁大距离移动容器元素指针,最好是刚从容器中释放的元素能马上让下一个元素使用,这时候栈就是一个很好的选择了。初始时将所有闲置对象指针压入栈,分配时pop,栈为空时返回空;释放时将对象指针push进栈即可。
实现
其实boost已经提供了对象池了,那为什么还要自己实现一个呢?当然是要方便DIY了…其实你也可以用boost的对象池来第二次封装
这部分直接参看附件源码吧
使用
这才是真正的重点
分配时直接用Sobot* p = ObjPool
new(p) Sobot()
你想在你的代码中充斥大量这样的代码吗?放到工厂里面也许是一种办法,但是工厂引用到了对象池了。而大师告诉我们好的设计要保持职责单一,用与不用对象池应该不影响原系统的正常运行。而且还有一点,用这种办法,就只能和某些组件绝缘了,比如智能指针。
此时重载new与delete就至关重要了:
static void* operator new(size_t) {
return SobotPool::instance().alloc();
}
static void operator delete(void* p) {
SobotPool::instance().free(reinterpret_cast
}
一个对象中往往充斥着大量指针,而这些指针指向的空间往往大于包含他们的对象本身。如果将这些指针所在在类也应用对象池,一方面是池的容量你无法估计,另一方面是使用起来麻烦。而且你也无法向上面这样给每个类注入new与delete的重载。用代理?呵呵,项目中估计会出一堆问题。这时候我们不妨使用脏数据,也就是说对象池中保存的对象全是可以直接使用的对象,而并非空对象,对象中的成员指针变量引用到的内存不在池中。为了保证安全,清空这些内存在池销毁时进行。
和上面的功能一起,我们可以定义一个宏,免得每次使用都得重复大量代码。如下:
#define USING_DIRTY_DATA true
// 如果不是方便测试需要,可以将这行
// typedef ObjPool
// 标注为private
#define DECLARE_USING_OBJ_POOL(obj_class, max_size, _using_dirty_data)
public:
typedef ObjPool
friend class obj_class##Pool;
static const bool using_dirty_data = _using_dirty_data;
public:
~obj_class() {
if (!_using_dirty_data) {this->purge();}
}
static void* operator new(size_t) {
return obj_class##Pool::instance().alloc();
}
static void operator delete(void* p) {
obj_class##Pool::instance().free(reinterpret_cast
}
static bool loadCache() {
while (true) {
obj_class* obj = new obj_class;
if (obj != NULL) {
if (!obj->init()) {
return false;
}
} else {
break;
}
};
obj_class##Pool::instance().freeAll();
return true;
}
调用时在类中加入如下代码:
// DECLARE_USING_OBJ_POOL(Bullet, BULLET_POOL_VOLUM, (NOT USING_DIRTY_DATA))
DECLARE_USING_OBJ_POOL(Bullet, BULLET_POOL_VOLUM, USING_DIRTY_DATA)
LoadCache是游戏加载阶段调用的,这里将进行所有池对象的初始化。为此,你还需要实现init和purge函数,分别是初始资源,销毁资源的,这些其实都只会被调用一次的。像状态的初始化,大可放构造函数中,每次使用对象构造函数都会被调用的。外界是不能直接操作pool的。
如果池容量过小,分配失败其实并不可怕。
见例子:
// 大规模测试
list
struct _Timer{
list
_Timer(list
void operator()() {
for (list
iter != _timer.end();) {
Entity* entity = *iter;
if (entity->isValid()) {
(*iter)->update();
} else {
entity->destroy();
iter = _timer.erase(iter);
continue;
}
++iter;
} // end for
}
} update_timer(timer);
const int num = 50;
log << endl << "大规模测试:" << endl;
for (int i = 0; i < num; ++i) {
Entity* entity = ObjManager
if (IS_VALID_POINTER(entity)) {
log << " alloced index:" << i << endl;
timer.push_back(entity);
} else {
log << " alloc bullet failed, waiting..." << endl;
// 失败了就多尝试一次,反正任务量是20个
--i;
}
update_timer();
}
// 不管使用什么模式都要自己回收所有的对象,
// 不要依赖于池析构时的对象释放
for (list
iter != timer.end(); ++iter) {
(*iter)->destroy();
}
池容量为3,这是运行结果:
[0sec] 加载缓存
[0sec] Bullet1 with HP:2
[0sec] init Bullet1
[0sec] Bullet2 with HP:3
[0sec] init Bullet2
[0sec] Bullet3 with HP:5
[0sec] init Bullet3
[0sec]
大规模测试:
[0sec] Bullet10 with HP:5
[0sec] alloced index:0
[0sec] Bullet11 with HP:1
[0sec] alloced index:1
[0sec] Bullet12 with HP:1
[0sec] alloced index:2
[0sec] destroy entity11
[0sec] Bullet13 with HP:2
[0sec] alloced index:3
[0sec] destroy entity12
[0sec] Bullet14 with HP:3
[0sec] alloced index:4
[0sec] alloc bullet failed, waiting…
[0sec] destroy entity10
[0sec] destroy entity13
[0sec] Bullet15 with HP:2
(这里省略很多行…)
[1sec] alloced index:46
[1sec] Bullet57 with HP:4
[1sec] alloced index:47
[1sec] alloc bullet failed, waiting…
[1sec] destroy entity55
[1sec] Bullet58 with HP:2
[1sec] alloced index:48
[1sec] alloc bullet failed, waiting…
[1sec] alloc bullet failed, waiting…
[1sec] destroy entity56
[1sec] destroy entity57
[1sec] destroy entity58
[1sec] Bullet59 with HP:5
[1sec] alloced index:49
[1sec] destroy entity59
[1sec]
释放池
[1sec] purge Bullet59
[1sec] freeing sprite buf. size:3
[1sec] purge Bullet56
[1sec] freeing sprite buf. size:2
[1sec] purge Bullet57
[1sec] freeing sprite buf. size:1
请按任意键继续. . .
相关文章
- 《燕云十六声》红尘无眼完成图文攻略 12-25
- 《燕云十六声》阴阳如影完成图文攻略 12-25
- 《燕云十六声》悬檐之下四架椽屋图文攻略 12-25
- 《燕云十六声》2024最新公测时间介绍 12-25
- 《燕云十六声》有没有藏宝阁 12-25
- 《燕云十六声》制作公司介绍 12-25