Atypical 2D game engine orx Source Code Reading Notes (3) Memory Management

Source: Internet
Author: User

Write by nine days Yan Ling (jtianling) -- blog.csdn.net/vagrxie

Discuss newsgroups and documents

Memory Management is the foundation of any game engine that hopes to achieve efficiency. I have done similar work before, so let's take a look at the implementation of orx, let's talk about our past experiences in similar work.

Memory Module

I personally feel that this is very unnecessary to become a module. In essence, there is no need to initialize any global variables, and there is no need to scan the tail at the exit, just a few functions, why are these functions in orx concentrated together as an orx module? Except for logical consistency, I feel that they are purely over-designed ........ At least from the current perspective.

Extern
Orxdllapi void
* Orxfastcall orxmemory_allocate (orxu32 _ u32size, orxmemory_type _ ememtype );
Extern
Orxdllapi void
Orxfastcall orxmemory_free (void
* _ Pmem );
Extern
Orxdllapi void
* Orxfastcall orxmemory_copy (void
* _ Pdest, const
Void
* _ Psrc, orxu32 _ u32size );
Extern
Orxdllapi void
* Orxfastcall orxmemory_move (void
* _ Pdest, void
* _ Psrc, orxu32 _ u32size );
Extern
Orxdllapi orxu32 orxfastcall orxmemory_compare (const
Void
* _ Pmem1, const
Void
* _ Pmem2, orxu32 _ u32size );
Extern
Orxdllapi void
* Orxfastcall orxmemory_set (void
* _ Pdest, orxu8 _ u8data, orxu32 _ u32size );
Extern
Orxdllapi void
* Orxfastcall orxmemory_zero (void
* _ Pdest, orxu32 _ u32size );
Extern
Orxdllapi void
* Orxfastcall orxmemory_reallocate (void
* _ Pmem, orxu32 _ u32size );


Besides allocate, all the other useful interfaces are directly packaged by the corresponding interfaces in the C language, and only one C statement is required.

From the memory type:
Typedef Enum _ orxmemory_type_t
{
Orxmemory_type_main = 0,/** <main memory type */

Orxmemory_type_video,/** <video memory type */
Orxmemory_type_sprite,/** <sprite memory type */
Orxmemory_type_background,/** <background memory type */
Orxmemory_type_palette,/** <palette memory type */

Orxmemory_type_config,/** <config memory */
Orxmemory_type_text,/** <text memory */

Orxmemory_type_temp,/** <temporary/scratch memory */

Orxmemory_type_number,/** <Number of memory type */

Orxmemory_type_none = orxenum_none/** <invalid memory type */

} Orxmemory_type;
We can see some of the author's ideas, but in fact there is no physically distinguished memory, (except for memory display) No matter how to divide by type, there is no practical value. In fact, orx does not care about this type either.

Void
* Orxfastcall orxmemory_allocate (orxu32 _ u32size, orxmemory_type _ ememtype)
{
/*
Module initialized?
*/

Orxassert (sstmemory. u32flags & orxmemory_ku32_static_flag_ready) = orxmemory_ku32_static_flag_ready );

/*
Valid parameters?
*/

Orxassert (_ ememtype <orxmemory_type_number );

/*
Returns system Allocation Function
*/

Return
(Void
*) Malloc (_ u32size ));
}


The meaningless encapsulation of standard functions in C language does not affect the efficiency of the individual. These C-standard functions are also cross-platform. What do they do? In terms of Optimization Efficiency, there are only a few elements that need to be encapsulated: malloc and free. (Realloc is not used much) unless the author of orx looks farther and hopes to include memory copy in the future.
Memory operation functions, including compare, all perform assembly-level optimization for specific CPU/platform.

Bank Module

This is the memory cache module used by orx to optimize performance.
The main structure is as follows:

/*
**************************************** **********************************

* Structure Declaration *

**************************************** **********************************
*/

Typedef
Struct
_ Orxbank_segment_t
{
Orxu32 * pu32cellallocationmap ;/*
* <List of BITs that represents free and used elements in the segment
*/

Void
* Initialize gmentdata ;/*
* <Pointer address on the head of the segment data cells
*/

Struct
_ Orxbank_segment_t * pstnext ;/*
* <Pointer on the next segment
*/

Orx2010u16nbfree ;/*
* <Number of free elements in the segment
*/

} Orxbank_segment;

Struct
_ Orxbank_t
{
Orxbank_segment * pstfirstsegment ;/*
* <First segment used in the bank
*/

Orxu32 u32flags ;/*
* <Flags set for the memory bank
*/

Orxmemory_type ememtype ;/*
* <Memory type that will be used by the memory allocation
*/

Orxu32 u32elemsize ;/*
* <Size of a cell
*/

Orx16nbcellpersegments ;/*
* <Number of cells per banks
*/

Orx2010u16sizesegmentbitfield ;/*
* <Number of u32 (4 bytes) to represent a segment
*/

Orxu32 u32counter ;/*
* <Number of allocated Cells
*/

};

Typedef
Struct
_ Orxbank_static_t
{
Orxu32 u32flags ;/*
* <Flags set by the memory module
*/

} Orxbank_static;

/*
**************************************** **********************************

* Module global variable *

**************************************** **********************************
*/

Static
Orxbank_static sstbank;

In terms of structure, the Bank should use _ orxbank_t
The structure is the main structure, representing a bank. Each bank can also contain an orxbank_segment
Type list. The actual memory is cached in orxbank_segment.
Class Object.

There are four main external interfaces:

/*
* Creates a new bank in memory and returns a pointer on it

* @ Param [in] _ u16nbelem number of elements per segments

* @ Param [in] _ u32size size of an element

* @ Param [in] _ u32flags flags set for this bank

* @ Param [in] _ ememtype memory type where the datas will be allocated

* @ Return returns a pointer on the memory bank

*/

Extern
Orxdllapi orxbank * orxfastcall orxbank_create (orx2010_u16nbelem, orxu32 _ u32size, orxu32 _ u32flags, orxmemory_type _ ememtype );

/*
* Frees a portion of memory allocated with orxmemory_allocate

* @ Param [in] _ pstbank pointer on the memory bank allocated by orx

*/

Extern
Orxdllapi void
Orxfastcall orxbank_delete (orxbank * _ pstbank );

/*
* Allocates a new cell from the bank

* @ Param [in] _ pstbank pointer on the memory bank to use

* @ Return a new cell of memory (orxnull if no allocation possible)

*/

Extern
Orxdllapi void
* Orxfastcall orxbank_allocate (orxbank * _ pstbank );

/*
* Frees an allocated Cell

* @ Param [in] _ pstbank bank of memory from where _ pcell has been allocated

* @ Param [in] _ pcell pointer on the cell to free

* @ Return a new cell of memory (orxnull if no allocation possible)

*/

Extern
Orxdllapi void
Orxfastcall orxbank_free (orxbank * _ pstbank, void
* _ Pcell );

Take a look at the process:
First, create a test project and modify it in the original orx independent application. The following code can almost complete all the major part of the code in the bank:

Struct
Mystruct {
Int
I;
};
Orxstatus gameapp: Init (){
Orxbank * Bank = orxbank_create (2
, Sizeof
(Mystruct), orxbank_ku32_flag_none, orxmemory_type_main );
Mystruct * P1 = (mystruct *) orxbank_allocate (bank );
Mystruct * P2 = (mystruct *) orxbank_allocate (bank );
Mystruct * P3 = (mystruct *) orxbank_allocate (bank );
Orxbank_free (bank, P1 );
Orxbank_free (bank, P2 );
Orxbank_free (bank, P3 );
Orxbank_delete (bank );

Return
Orxstatus_success;
}
Orxbank_create and orxbank_delete are a pair used to create and delete a bank object.
Then you can use orxbank_allocate
, Orxbank_free
To obtain or release the memory. It is easy to use. For those who do not want to write their own memory management module, even their own projects can use this memory management part, because this is the most underlying code in orx, it is not difficult to strip out without relying on too many other things.




This part of memory can be said to be a lot of tips and a very dirty part, almost all of which are one byte-related content, I started from writing memory management modules and file systems and used to think of binary memory/file data. So here I am not interested in understanding orx's practice with just one sentence.



Procedure: Use orxbank_create
When creating a bank, you will use orxbank_segmentcreate to create a specified number of cache segments. The size of each cell is of course specified by 2nd parameters. In fact, each segment is a list and stores orxbank_create
The first parameter specifies the number of cells. By default, when the cache in a segment is allocated, a new segment will be automatically allocated. The number of cells in the new segment will be the same, and the number of cells in the new segment will be the same.
String member variables.

Actual Memory Allocation Structure:
The actual size of each segment allocation is:
/* Compute the segment size */
U32segmentsize = sizeof (orxbank_segment) +/* size of the Structure */
_ Pstbank-> u16sizesegmentbitfield * sizeof (orxu32) +/* size of bitfields */
_ Pstbank-> u16nbcellpersegments * _ pstbank-> u32elemsize;/* size of stored data */

Overall usage:

/*
Allocate a new segent of memory
*/

Pstsegment = (orxbank_segment *) orxmemory_allocate (u32segmentsize, _ pstbank-> ememtype );
If
(Pstsegment! = Orxnull)
{
/*
Set initial segment values
*/

Orxmemory_zero (pstsegment, u32segmentsize );
Pstsegment-> pstnext = orxnull;
Pstsegment-> u16nbfree = _ pstbank-> u16nbcellpersegments;
Pstsegment-> pu32cellallocationmap = (orxu32 *) (orxu8 *) pstsegment) + sizeof
(Orxbank_segment ));
Pstsegment-> merge gmentdata = (void
*) (Orxu8 *) pstsegment-> pu32cellallocationmap) + (_ pstbank-> u16sizesegmentbitfield * sizeof
(Orxu32 )));
}


It is equivalent to allocating the segment's own memory together with the memory to be allocated externally. Each time we allocate the memory in front of the sizeof (orxbank_segment), we actually use the segment structure object, the following _ pstbank-> u16sizesegmentbitfield * sizeof (orxu32) bytes memory is used as a flag array to indicate whether the corresponding memory segment has been allocated, is the memory actually returned to the user.

Summary

The memory management module of orx is suitable for Cache Usage, and is easy to use. Similar technologies are also very common.
However, I personally feel that a similar method is only applicable to people who have been accustomed to using this method all the year round. The function of adding cache in this way is totally under the pressure of every programmer, I don't feel very suitable. The best way is to implement relevant functions transparently at the lowest level, rather than imposing performance requirements on everyone. Second, for existing projects, using similar technologies requires adding special code to each part of the cache, which is not very convenient. Also, it is difficult to modify the cached code in every corner of the project. In orx, the memory and bank modules are almost depended on by any other module. It is hard to imagine what to do when it needs to be changed.

In fact, it is not difficult to do a more humane and better way. To put it simply, malloc is directly replaced in C language, and the implementation of free is to overload new in C ++, the delete operator directly uses the memory size to perform user-level caching without considering the purpose of each memory allocation (such as the memory type in orx ), the actual memory allocation module does not really need to consider and care about what the user uses the memory for at last. It just needs the length of memory for the user. This increases the coupling between the two aspects.
The size-based Cache can be very simple or complex. Generally, memory with a small size can be chained separately. to limit the number of linked lists, the two-power method can be used. For example, a chain table storing sizes such as 4, 8, 16, 32, and 64 is directly retrieved from the chain table for each memory allocation. If the allocated size is between two sizes, you can directly return a larger memory. For example, if a user allocates a 9-byte memory and returns 16 bytes to it, although this results in a certain amount of memory waste, This is not unacceptable considering the efficiency and implementation issues. In addition, for C ++, you can also reload the standard library container distributor. For the above method and the method of reloading the container distributor, refer to the sgi stl implementation. (Detailed description in Hou Jie STL source code analysis)
In fact, memory management is not only for efficiency considerations in C/C ++, but also for Memory leakage detection (especially implicit) in large projects.
Finally, all the information I have seen shows that the efficiency of automatic garbage collection in C language is very high, and it is even more efficient than manual memory management written by ordinary people, in this case, an automatic or semi-automatic garbage collection mechanism is also a good choice.
In short, although I used to write a lot of code similar to orx for manual Cache Management when writing server code (usually taking space for time, I think there are still many better alternatives.

 

The author of the original article retains the copyright reprinted. Please indicate the original author and give a link

Write by nine days Yan Ling (jtianling) -- blog.csdn.net/vagrxie

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: info-contact@alibabacloud.com 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.