1 / 16

Memory and I/O Systems

Memory and I/O Systems. Memory Management Automatic memory management (Java/C#) vs. manual memory management (C++) Working with memory Safety (not program crashing) A memory leak is an allocation of memory that is “forgotten” by the program and never used again.

etan
Download Presentation

Memory and I/O Systems

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Memory and I/O Systems Memory Management Automatic memory management (Java/C#) vs. manual memory management (C++) Working with memory Safety (not program crashing) A memory leak is an allocation of memory that is “forgotten” by the program and never used again. Corrupt memory happens when some part of the program starts overwriting memory locations that it’s not sup-posed to. Knowledge about memory allocation and de-allocation Control over memory allocation where memory blocks are allocated (consecutive memories for multiple objects for better caching) how memory blocks are allocated (especially for small objects) Memory fragmentation A virtual addressing system will greatly help reduce this problem.

  2. Memory and I/O Systems Memory Management Static allocation A program could be designed so it never uses new and delete (or malloc and free), and relies exclusively on statically allocated objects or objects created on the stack. Advantage: better performance, straightforward, address all the safety, knowledge, and control issues, no dangling pointers (but invalid objects) Drawback: wasted memory, unpredictable quantity of objects, objects need to be prepared to be statically initialized, with all its consequences.

  3. Memory and I/O Systems Memory Management Dynamic allocation Allocations happen at seemingly random times, for random amounts of memory. Process of a memory allocation request: Everything starts with an innocent-looking object creation in the code: SpecialEffect * pEffect = new SpecialEffect( ) ; 2. The compiler, internally, substitutes that call with two separate calls: one to allocate the correct amount of memory, and one to call the constructor of the SpecialEffect class: SpecialEffect * pEffect = new ( sizeof( SpecialEffect) ) ; pEffect- >SpecialEffect( ) ; 3. The global operator new must then allocate the amount of requested memory. In most of the standard implementations, the global operator new simply calls the malloc function: void * operator new ( unsigned intnSize ) { return malloc( nSize) ; } malloc is not an atomic operation. Instead, it will call platform-specific memory-allocation functions to allocate the correct amount from the heap. Global operator delete follows a similar sequence, but calls the destructor and free instead of the constructor and malloc.

  4. Memory and I/O Systems Memory Management Customer Memory Manager Global operators New and Delete Overriding the global operators new and delete. void * operator new ( size_t size, Heap * pHeap) { size_tnRequestedBytes = size + sizeof( AllocHeader) ; char * pMem = ( char *) malloc( nRequestedBytes) ; AllocHeader * pHeader = ( AllocHeader *) pMem; pHeader- >pHeap = pHeap; pHeader- >nSize = size; pHeap- >AddAllocation ( size) ; void * pStartMemBlock = pMem + sizeof( AllocHeader) ; return pStartMemBlock; } void operator delete ( void * pMem) { AllocHeader * pHeader = ( AllocHeader *) ( ( char *) pMem – sizeof( AllocHeader) ) ; pHeader- >pHeap- >RemoveAllocation ( pHeader- >nSize) ; free( pHeader) ; }

  5. Memory and I/O Systems Memory Management Customer Memory Manager Class-specific operators New and Delete class GameObject { public: / / All the normal declarations. . . static void * operator new( size_t size) ; static void operator delete( void * p, size_t size) ; private: static Heap * s_pHeap; } ; void * GameObject: : operator new( size_t size) { if ( s_pHeap==NULL) { s_pHeap = HeapFactory: : CreateHeap(“ Game object” ) ; } return : : operator new( size, s_pHeap) ; } void GameObject: : operator delete( void * p, size_t size) { : : operator delete( p) ; }

  6. Memory and I/O Systems Memory Management Customer Memory Manager Error checking Make sure the memory we are about to free was allocated through our memory manager. One common mistake when dealing with dynamically allocated memory, especially in the form of an array, is to write past the end of the allocated block. Detecting memory leaks The concept of finding memory leaks is simple: at one point in time, we take a bookmark of the memory status, and then later we take another bookmark and report all memory allocations that were present the second time but not the first time.

  7. Memory and I/O Systems Memory Management Customer Memory Manager Memory pools A memory pool is a certain amount of preallocated memory that will be used to allocate objects of a particular size at runtime. Whenever one of those objects is released by the program, its memory is returned to the pool, not freed back to the heap. Advantages: no performance hit, no head fragmentation, one large allocation Disadvantage: memory pools will usually have some unused space (slack space).

  8. Memory and I/O Systems File I/O We want to load files very quickly We want to be able to access and manipulate files in the same way across all different platforms. Creating our own custom file system lets us address those issues. Unified, platform-independent file system class FileSystem { public: File * Open ( const std: : string & filename) ; boolDoesFileExist ( const std: : string &filename) const; bool Mount ( const std: : string & src, const std: : string & dest) ; boolUnmount ( const std: : string & src) ; } ; Files class File { public: File ( const std: : string & filename) ; int Read( byte * pData, intbytesToRead) ; int Write( byte * pData, intbytesToWrite) ; int Seek( intdesiredPosition) ; template<typename T> int Read ( T * pData, uintnCount = 1 ) { uintnRead = ReadRaw( ( byte *) pData, uint( nCount * sizeof( T) ) ) ; return nRead/ sizeof( T) ; } } ;

  9. Memory and I/O Systems File I/O Unified, platform-independent file system Buffering One of the goals we had for creating our own file system was to achieve great performance. Buffering is a key element toward achieving that goal. The cleanest way to implement a buffering scheme is by using a layered approach. The BufferingLayer class looks like this: class BufferingLayer : public DataStream { public: BufferingLayer ( DataStream & sourceStream, intbufferSize) ; virtual int Read( byte * pData, intbytesToRead) ; virtual int Write( byte * pData, intbytesToWrite) ; virtual int Seek( intdesiredPosition) ; private: DataStream & m_stream; byte * pBuffer; } ;

  10. Memory and I/O Systems File I/O Pack files A pack file is simply a large file that contains other files. It probably contains some header information with a list of all the files contained inside and where they are located (their offset from the origin of the file), plus a large chunk of data with the contents of every file. class RangeLayer : public DataStream { public: RangeLayer ( DataStream & sourceStream, int offset, int length) ; virtual int Read( byte * pData, intbytesToRead) ; virtual int Write( byte * pData, intbytesToWrite) ; virtual int Seek( intdesiredPosition) ; private: DataStream & m_stream; intm_offset; intm_length; } ; new RangeLayer( packFileStream, file. Offset, file. Length) ;

  11. Memory and I/O Systems Game Resources A game resource, also called a “game asset,” is anything that gets loaded from a disc and could be shared by several parts of the game: a texture, an animation, a sound, and so forth. Working with game resources Do we need a dedicated unit in a game program to manage game recourses? Why? Resource manager The first thing that we need is to create the concept of a generic game resource. class I Resource { public: virtual ~I Resource( ) { } ; virtual const std: : string & GetName( ) const = 0; virtual ResourceTypeGetType( ) const = 0; } ; We can finally create a resource manager, which will be in charge of loading resources, remembering all resources currently loaded, and returning references to them whenever anybody asks. class ResourceManager { public: Texture * CreateTexture( const std: : string & filename) ; Animation * CreateAnimation( const std: : string & filename) ; Mesh * CreateMesh( const std: : string & filename) ; / / . . . private: I Resource * CreateResource( const std: : string & filename) ; typedef std: : map<std: : string, I Resource *> ResourceMap; ResourceMapm_resources; }

  12. Memory and I/O Systems Game Resources Resource lifetime When a game resource is not being used, should it be destroyed or kept for future use? All at once We simply want to load all the resources for a level at the beginning of that level and keep them around until we exit the level. Explicit lifetime management We could take it upon ourselves to manage the lifetime of each resource manually. How to know a game resource is no longer needed? Reference counting Reference counting attempts to address the problem of shared ownership of resources. Each resource keeps track of how many parts of the code are holding a reference to it. As long as that reference is greater than zero, the resource is needed somewhere. Whenever it reaches zero, it means that nobody is using it, and it can be safely deleted if we want to. It cannot predicate future referencing.

  13. Memory and I/O Systems Game Resources Resources and instances Resources are all the data that could be shared among different parts of the game. An instance is any data associated with the resource that is unique for each occurrence of the resource in the game. Keeping the two concepts clearly separated will help us use resources correctly. Example distinction: skeletal mesh vs. its position and orientation In other words: A resources’ varying states are considered as its instances. (class vs. its instances) Resource precaching The resources of the objects that will be spawned during gameplay, but are not necessarily part of the original layout of the level. Precaching involves loading resources for objects that we don’t need right away, but we know we will need later. Any resources that will be needed in the middle of the game are going to be requested by entities that are already in the level. The game entity simply needs to tell the system to keep it around in case we use it later. The game can keep a list of all the precached resources for the level.

  14. Memory and I/O Systems Serialization Saving It is best if each entity decides for itself how to best save itself to the stream. ISerializable interface class I Serializable { public: virtual ~I Serializable( ) { } ; virtual bool Write( I Stream & stream) const = 0; virtual bool Read( I Stream & stream) = 0; } ; Implementing Write boolGameCamera: : Write( I Stream & stream) const { / / Let the parent class write common things like position, rotation, etc. boolbSuccess = GameEntity: : Write( stream) ; / / These are basic data types, serialize them directly bSuccess &= WriteFloat( stream, m_FOV) ; bSuccess &= WriteFloat( stream, m_NearPlane) ; bSuccess &= WriteFloat( stream, m_FarPlane) ; / / This is an object that needs to be serialized in turn bSuccess &= m_lens. Write( stream) ; return bSuccess; }

  15. Memory and I/O Systems Serialization Saving Unique identifiers What to do about saving pointers. Method: Completely avoid pointers, or at least pointers to other game entities. Instead of a pointer, we could refer to any other game entities through unique IDs (or UIDs). GameEntity * pTarget = GetEntityFromUI D( m_targetUI D) ; Resources What about pointers to game resources instead of entities? Usually, entities will refer to resources by filename, or by some resource ID. Saving pointers Changing an existing code base from using pointers to using UIDs can be quite a task. We can save the pointers straight to disk. However, the memory address contained in the pointer will not point to the correct memory location when we load the game again.

  16. Memory and I/O Systems Serialization Loading Creating objects A requirement when restoring different types of objects is to be able to create any object type based on the data we read from the stream. Also, we need to know what class/type the loaded object, as we need to actually create an object of that type. Depending on what type of factory system we have, we can save full strings for the class name of our entities and create them again by passing the strings to the factory system. Loading pointers If, along with every entity, we also store its memory location when it was saved, we can construct a translation table at load time that can allow us to go from the old memory addresses to the new memory addresses. The load process is as follows: (1) load all the entities and construct a table mapping old addresses to new ones. (2) Make a “fix-up” pass through all the entities and give them a chance to fix any pointers they have to point to the new, correct memory locations.

More Related