Solution for accelerating game memory allocation by object pool

Source: Internet
Author: User

Compared with the memory pool, the object pool is easier to use and manage, and dirty data can also be used, that is, the data of the object that was recycled last time. In addition, the occasional space allocation failure is not that important (how to complete the allocation task in case of failure will be explained later), and the speed in the game is more important.


If you apply for a large amount of continuous memory (the size of an integer object) at a time, you 'd better use the heap. Of course, if you use the Stack array, no one will block you, but the stack space is quite limited...

Because the lifetime of the allocated object is not fixed (as shown in the figure below), the pool cannot maintain the continuity of the allocated object. In this case, moving blocks will reduce the program efficiency.

  Allocate       Allocate Allocate   Allocate  

So we need to put the pointer of the idle object into the container for management. This container must be able to quickly access and delete, and do not need to move the element pointer of the container from a long distance. It is best to release the elements from the container and use the next element immediately, at this time, stack is a good choice. Initially, all idle object pointers are pushed to the stack. If the stack is empty, the system returns null. When the stack is empty, the system pushes the object pointer to the stack.


Actually, boost already provides an object pool. Why do I need to implement one myself? Of course it is convenient for DIY... In fact, you can also use the boost object pool for the second encapsulation.

For this part, see the source code of the attachment.


This is the real focus

Directly use Sobot * p = ObjPool <Sobot>: alloc ()? No, you should also use placement new to call its constructor:

New (p) Sobot ()

Do you want to fill your code with a lot of such code? Put it in the factory may be a way, but the factory references the object pool. The master told us that a good design should maintain a single responsibility, and the normal operation of the original system should not be affected by the use and non-use object pool. In addition, this method can only be insulated from some components, such as smart pointers.

In this case, overloading new and delete is crucial:

Static void * operator new (size_t ){

Return SobotPool: instance (). alloc ();


Static void operator delete (void * p ){

SobotPool: instance (). free (reinterpret_cast <Sobot *> (p ));


An object is often filled with a large number of pointers, and these pointers usually point to more space than the object itself. If you want to apply these pointers to the object pool in the class, you cannot estimate the size of the pool, or use it in a troublesome way. In addition, you cannot inject a new or delete overload to each class. Use proxy? A bunch of problems may occur in the project. In this case, we may wish to use dirty data. That is to say, the objects stored in the object pool are all objects that can be directly used, rather than empty objects. The member pointer variables referenced in the object are not in the pool. To ensure security, clear the existing pool and destroy it.

Together with the above functions, we can define a macro to avoid repeated code every time. As follows:

# Define USING_DIRTY_DATA true

// If it is not convenient for testing, you can set this line

// Typedef ObjPool <obj_class, max_size> obj_class # Pool;

// Mark it as private

# Define DECLARE_USING_OBJ_POOL (obj_class, max_size, _ using_dirty_data)


Typedef ObjPool <obj_class, max_size> obj_class # Pool;

Friend class obj_class # Pool;

Static const bool using_dirty_data = _ using_dirty_data;


~ 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 <obj_class *> (p ));


Static bool loadCache (){

While (true ){

Obj_class * obj = new obj_class;

If (obj! = NULL ){

If (! Obj-> init ()){

Return false;


} Else {




Obj_class # Pool: instance (). freeAll ();

Return true;


Add the following code to the class during the call:



LoadCache is called during the game loading phase. All pool objects will be initialized here. To this end, you also need to implement the init and purge functions, which are the initial resources and destroy the resources, which are actually called only once. Initialization of the image status, which can be put into the constructor. Every time an object constructor is used, it will be called. The outside world cannot directly operate the pool.

If the pool capacity is too small, the allocation failure is not terrible.

See the example below:

// Large-scale test

List <Entity *> timer;

Struct _ Timer {

List <Entity *> & _ timer;

_ Timer (list <Entity *> & timer): _ timer (timer ){}

Void operator ()(){

For (list <Entity * >:: iterator iter = _ timer. begin ();

Iter! = _ Timer. end ();){

Entity * entity = * iter;

If (entity-> isValid ()){

(* Iter)-> update ();

} Else {

Entity-> destroy ();

Iter = _ timer. erase (iter );



++ Iter;

} // End


} Update_timer (timer );

Const int num = 50;

Log <endl <"large-scale test:" <endl;

For (int I = 0; I <num; ++ I ){

Entity * entity = ObjManager <Entity>: instance (). make ("Bullet ");

If (IS_VALID_POINTER (entity )){

Log <"alloced index:" <I <endl;

Timer. push_back (entity );

} Else {

Log <"alloc bullet failed, waiting..." <endl;

// If it fails, try again. The number of tasks is 20.

-- I;


Update_timer ();


// Retrieve all objects by yourself regardless of the mode used,

// Do not rely on the release of objects in the pool destructor

For (list <Entity * >:: iterator iter = timer. begin ();

Iter! = Timer. end (); ++ iter ){

(* Iter)-> destroy ();


The pool capacity is 3, which is the result:
[0sec] loading cache
[0sec] Bullet1 with HP: 2
[0sec] init Bullet1
[0sec] Bullet2 with HP: 3
[0sec] init Bullet2
[0sec] Bullet3 with HP: 5
[0sec] init Bullet3
Large-scale testing:
[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
(Many rows are omitted here ...)
[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
Release Pool
[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
Press any key to continue...

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.