├── dcm_pool ├── test.cpp ├── include │ └── dcm_pool │ │ ├── _object_ptr_imp.h │ │ ├── _holes_list_imp.h │ │ ├── _object_in_pool_imp.h │ │ ├── exceptions.h │ │ ├── holes_list.h │ │ ├── defs.h │ │ ├── object_in_pool.h │ │ ├── object_ptr.h │ │ ├── _dcm_pool_imp.h │ │ └── dcm_pool.h ├── dcm_pool.vcxproj.filters └── dcm_pool.vcxproj ├── LICENSE ├── dcm_pool.sln ├── .gitignore └── README.md /dcm_pool/test.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonenNess/dcm_pool/HEAD/dcm_pool/test.cpp -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ronen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dcm_pool/include/dcm_pool/_object_ptr_imp.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file include\dcm_pool\_object_ptr_imp.h. 3 | * 4 | * \brief Implement the ObjectPtr template class. 5 | * \license MIT 6 | * \autor Ronen Ness. 7 | * \since 2018 8 | */ 9 | 10 | #include "object_ptr.h" 11 | 12 | #ifndef __OBJECT_PTR_IMP__ 13 | #define __OBJECT_PTR_IMP__ 14 | #include "dcm_pool.h" 15 | 16 | namespace dcm_pool 17 | { 18 | template 19 | ObjectPtr::ObjectPtr(DcmPool* pool, ObjectId id) : 20 | _pool(pool), 21 | _id(id), 22 | _pool_defrag_version(-1) 23 | { 24 | } 25 | 26 | template 27 | ObjectId ObjectPtr::_get_id() const 28 | { 29 | return _id; 30 | } 31 | 32 | template 33 | T& ObjectPtr::operator*(void) 34 | { 35 | // check if we have a valid cached pointer to return 36 | if (_pool_defrag_version == _pool->_get_defrags_count()) 37 | return *_cached_ptr; 38 | 39 | // if not get the pointer and cache it 40 | T* ret = &(_pool->_get_object(_id)); 41 | _cached_ptr = ret; 42 | _pool_defrag_version = _pool->_get_defrags_count(); 43 | 44 | // return pointer 45 | return *ret; 46 | } 47 | 48 | template 49 | T* ObjectPtr::operator->(void) 50 | { 51 | return &(this->operator*()); 52 | } 53 | } 54 | 55 | #endif -------------------------------------------------------------------------------- /dcm_pool.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dcm_pool", "dcm_pool\dcm_pool.vcxproj", "{10589364-1700-46A8-808E-5A31187B1FC7}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {10589364-1700-46A8-808E-5A31187B1FC7}.Debug|x64.ActiveCfg = Debug|x64 17 | {10589364-1700-46A8-808E-5A31187B1FC7}.Debug|x64.Build.0 = Debug|x64 18 | {10589364-1700-46A8-808E-5A31187B1FC7}.Debug|x86.ActiveCfg = Debug|Win32 19 | {10589364-1700-46A8-808E-5A31187B1FC7}.Debug|x86.Build.0 = Debug|Win32 20 | {10589364-1700-46A8-808E-5A31187B1FC7}.Release|x64.ActiveCfg = Release|x64 21 | {10589364-1700-46A8-808E-5A31187B1FC7}.Release|x64.Build.0 = Release|x64 22 | {10589364-1700-46A8-808E-5A31187B1FC7}.Release|x86.ActiveCfg = Release|Win32 23 | {10589364-1700-46A8-808E-5A31187B1FC7}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {54004CCD-0A62-43AF-ABF2-8263657D31DB} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /dcm_pool/include/dcm_pool/_holes_list_imp.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file include\dcm_pool\_holes_list.h. 3 | * 4 | * \brief Implement the HolesList template class. 5 | * \license MIT 6 | * \autor Ronen Ness. 7 | * \since 2018 8 | */ 9 | 10 | #ifndef __HOLES_LIST_IMP__ 11 | #define __HOLES_LIST_IMP__ 12 | 13 | namespace dcm_pool 14 | { 15 | namespace _internal 16 | { 17 | template 18 | void HolesList::push_back(size_t hole_index) 19 | { 20 | // if not empty, take the current first index and set it as the id of the new hole 21 | if (_size) 22 | { 23 | _objects[hole_index].set_id(_first_index); 24 | } 25 | 26 | // set new first index and increase size 27 | _first_index = hole_index; 28 | _size++; 29 | } 30 | 31 | template 32 | size_t HolesList::pop_back() 33 | { 34 | // if size is 0, exception 35 | if (!_size) 36 | { 37 | throw new std::out_of_range("Objects pool holes list out of range!"); 38 | } 39 | 40 | // special case - if its last item just return _first_index and zero size 41 | if (_size == 1) 42 | { 43 | _size = 0; 44 | return _first_index; 45 | } 46 | 47 | // get index to return 48 | size_t to_ret = _first_index; 49 | 50 | // set the new first index 51 | _first_index = _objects[to_ret].get_id(); 52 | 53 | // decrease size and return index 54 | _size--; 55 | return to_ret; 56 | } 57 | 58 | template 59 | void HolesList::clear() 60 | { 61 | _size = 0; 62 | } 63 | } 64 | } 65 | 66 | #endif -------------------------------------------------------------------------------- /dcm_pool/include/dcm_pool/_object_in_pool_imp.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file include\dcm_pool\_object_in_pool_imp.h. 3 | * 4 | * \brief Implement the ObjectInPool template class. 5 | * \license MIT 6 | * \autor Ronen Ness. 7 | * \since 2018 8 | */ 9 | 10 | #include "object_in_pool.h" 11 | 12 | #ifndef __OBJECT_IN_POOL_IMP__ 13 | #define __OBJECT_IN_POOL_IMP__ 14 | 15 | namespace dcm_pool 16 | { 17 | namespace _internal 18 | { 19 | 20 | template 21 | ObjectInPool::ObjectInPool(ObjectId id) : _id(id), _is_used(false) 22 | { 23 | } 24 | 25 | template 26 | ObjectInPool::ObjectInPool(const ObjectInPool& other) 27 | { 28 | (*this) = other; 29 | } 30 | 31 | template 32 | ObjectId ObjectInPool::get_id() const 33 | { 34 | return _id; 35 | } 36 | 37 | template 38 | void ObjectInPool::set_id(ObjectId id) 39 | { 40 | _id = id; 41 | } 42 | 43 | template 44 | T& ObjectInPool::get_object() 45 | { 46 | return _obj; 47 | } 48 | 49 | template 50 | bool ObjectInPool::is_used() const 51 | { 52 | return _is_used; 53 | } 54 | 55 | template 56 | void ObjectInPool::set_is_used(bool is_used) 57 | { 58 | _is_used = is_used; 59 | } 60 | 61 | template 62 | ObjectInPool& ObjectInPool::operator=(const ObjectInPool& other) 63 | { 64 | _obj = other._obj; 65 | _is_used = other._is_used; 66 | _id = other._id; 67 | return *this; 68 | } 69 | 70 | template 71 | ObjectInPool& ObjectInPool::operator=(ObjectInPool&& other) 72 | { 73 | _obj = std::move(other._obj); 74 | _is_used = other._is_used; 75 | _id = other._id; 76 | other._is_used = false; 77 | other._id = ObjectPoolMaxIndex; 78 | return *this; 79 | } 80 | } 81 | } 82 | 83 | #endif -------------------------------------------------------------------------------- /dcm_pool/include/dcm_pool/exceptions.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file include\dcm_pool\exceptions.h. 3 | * 4 | * \brief Define internal exceptions that Fast Objects Pool may throw. 5 | * \license MIT 6 | * \autor Ronen Ness. 7 | * \since 2018 8 | */ 9 | 10 | 11 | #pragma once 12 | #include 13 | 14 | namespace dcm_pool 15 | { 16 | /*! 17 | * \struct ExceededPoolLimit 18 | * 19 | * \brief Raised when tried to allocate more objects than limited in pool. 20 | * 21 | * \author Ronen 22 | * \date 2/21/2018 23 | */ 24 | struct ExceededPoolLimit : public std::exception 25 | { 26 | const char * what() const throw () 27 | { 28 | return "Exceeded pool max limit!"; 29 | } 30 | }; 31 | 32 | /*! 33 | * \struct AccessViolation 34 | * 35 | * \brief Raised for when someone tries to use or release an object that is not in used. 36 | * 37 | * \author Ronen 38 | * \date 2/21/2018 39 | */ 40 | struct AccessViolation : public std::exception 41 | { 42 | const char * what() const throw () 43 | { 44 | return "Tried to release or use a released object!"; 45 | } 46 | }; 47 | 48 | /*! 49 | * \struct CannotResizeWhileNotDefragged 50 | * 51 | * \brief Raised for when someone tries to resize pool but it has holes to defrag. 52 | * 53 | * \author Ronen 54 | * \date 2/21/2018 55 | */ 56 | struct CannotResizeWhileNotDefragged : public std::exception 57 | { 58 | const char * what() const throw () 59 | { 60 | return "Cannot resize pool while there are holes to defrag!"; 61 | } 62 | }; 63 | 64 | /*! 65 | * \struct InternalError 66 | * 67 | * \brief Raised for internal errors or corrupted pool data. 68 | * 69 | * \author Ronen 70 | * \date 2/21/2018 71 | */ 72 | struct InternalError : public std::exception 73 | { 74 | const char * what() const throw () 75 | { 76 | return "Internal error or corrupted data!"; 77 | } 78 | }; 79 | } -------------------------------------------------------------------------------- /dcm_pool/dcm_pool.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {b0ddda2e-2b19-411b-b91d-14580dd90a57} 18 | 19 | 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files\dcm_pool 26 | 27 | 28 | Header Files\dcm_pool 29 | 30 | 31 | Header Files\dcm_pool 32 | 33 | 34 | Header Files\dcm_pool 35 | 36 | 37 | Header Files\dcm_pool 38 | 39 | 40 | Header Files\dcm_pool 41 | 42 | 43 | Header Files\dcm_pool 44 | 45 | 46 | Header Files\dcm_pool 47 | 48 | 49 | Header Files\dcm_pool 50 | 51 | 52 | Header Files\dcm_pool 53 | 54 | 55 | 56 | 57 | Source Files 58 | 59 | 60 | -------------------------------------------------------------------------------- /dcm_pool/include/dcm_pool/holes_list.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file include\dcm_pool\holes_list.h. 3 | * 4 | * \brief An internal list of holes in pool without wasting additional memory. 5 | * \license MIT 6 | * \autor Ronen Ness. 7 | * \since 2018 8 | */ 9 | 10 | 11 | #pragma once 12 | 13 | #include 14 | #include "object_in_pool.h" 15 | #include "defs.h" 16 | 17 | 18 | using namespace std; 19 | 20 | namespace dcm_pool 21 | { 22 | namespace _internal 23 | { 24 | /*! 25 | * \class DcmPool 26 | * 27 | * \brief An internal object used to hold a vector of holes in a pool, without wasting any additional memory. 28 | * This list makes use of the objects-in-pool header of the already free objects. 29 | * 30 | * \author Ronen 31 | * \date 2/21/2018 32 | * 33 | * \tparam T Type of objects to place in pool. 34 | */ 35 | template 36 | class HolesList 37 | { 38 | private: 39 | 40 | // holes list current size. 41 | size_t _size; 42 | 43 | // the first index used to store holes in the vector 44 | size_t _first_index; 45 | 46 | // the actual vector of objects we use. 47 | vector >& _objects; 48 | 49 | public: 50 | 51 | /*! 52 | * \fn HolesList::HolesList(vector >& objects) 53 | * 54 | * \brief Constructor 55 | * 56 | * \author Ronen 57 | * \date 3/8/2018 58 | * 59 | * \param [in,out] objects The objects. 60 | */ 61 | HolesList(vector >& objects) : _objects(objects), _size(0) { } 62 | 63 | /*! 64 | * \fn inline size_t HolesList::size() const 65 | * 66 | * \brief Gets the size of the holes list. 67 | * 68 | * \author Ronen 69 | * \date 3/8/2018 70 | * 71 | * \return List size. 72 | */ 73 | inline size_t size() const { return _size; } 74 | 75 | /*! 76 | * \fn void HolesList::push_back(size_t hole_index); 77 | * 78 | * \brief Pushes a hole index into the holes list. 79 | * 80 | * \author Ronen 81 | * \date 3/8/2018 82 | * 83 | * \param hole_index Zero-based index of the hole. 84 | */ 85 | void push_back(size_t hole_index); 86 | 87 | /*! 88 | * \fn void HolesList::pop_back(); 89 | * 90 | * \brief Pops the last hole from the list. 91 | * 92 | * \author Ronen 93 | * \date 3/8/2018 94 | */ 95 | size_t pop_back(); 96 | 97 | /*! 98 | * \fn void HolesList::clear(); 99 | * 100 | * \brief Clears this list. 101 | * 102 | * \author Ronen 103 | * \date 3/8/2018 104 | */ 105 | void clear(); 106 | }; 107 | } 108 | } 109 | 110 | #include "_holes_list_imp.h" -------------------------------------------------------------------------------- /dcm_pool/include/dcm_pool/defs.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file include\dcm_pool\defs.h. 3 | * 4 | * \brief Basic defs for the pool objects. 5 | * \license MIT 6 | * \autor Ronen Ness. 7 | * \since 2018 8 | */ 9 | 10 | 11 | #pragma once 12 | 13 | 14 | using namespace std; 15 | 16 | namespace dcm_pool 17 | { 18 | // predeclare objects pool 19 | template 20 | class DcmPool; 21 | 22 | /*! \brief Invalid index / max object id. */ 23 | const size_t ObjectPoolMaxIndex = std::numeric_limits::max(); 24 | 25 | /*! 26 | * \typedef unsigned int ObjectId 27 | * 28 | * \brief Represent an internal object id while in objects pool. 29 | */ 30 | typedef size_t ObjectId; 31 | 32 | /*! 33 | * \enum IterationReturnCode 34 | * 35 | * \brief Different return code the iteration callback can return. 36 | */ 37 | enum IterationReturnCode 38 | { 39 | ITER_CONTINUE, 40 | ITER_BREAK 41 | }; 42 | 43 | /*! 44 | * \typedef void(*pool_iterator)(T&, ObjectId, DcmPool&) 45 | * 46 | * \brief Callback used to iterate objects pool with extended options. 47 | */ 48 | template 49 | using PoolIteratorEx = IterationReturnCode(*)(T&, ObjectId, DcmPool&); 50 | 51 | /*! 52 | * \typedef void(*pool_iterator)(T&, ObjectId) 53 | * 54 | * \brief A simple callback used to iterate objects pool. 55 | */ 56 | template 57 | using PoolIterator = void(*)(T&, ObjectId); 58 | 59 | /*! 60 | * \typedef void(*pool_iterator)(const T&, ObjectId, const DcmPool&) 61 | * 62 | * \brief Callback used to iterate objects pool with extended options. 63 | */ 64 | template 65 | using ConstPoolIteratorEx = IterationReturnCode(*)(const T&, ObjectId, const DcmPool&); 66 | 67 | /*! 68 | * \typedef void(*pool_iterator)(const T&, ObjectId) 69 | * 70 | * \brief A simple callback used to iterate objects pool. 71 | */ 72 | template 73 | using ConstPoolIterator = void(*)(const T&, ObjectId); 74 | 75 | /*! \brief Callback to handle different pool events like allocating new object or releasing an object. */ 76 | template 77 | using EventsHandler = void(*)(T&, ObjectId, DcmPool&); 78 | 79 | /*! 80 | * \enum DefragModes 81 | * 82 | * \brief Different ways we can handle fragmentation in our objects pool, eg 'holes' we need to close after releasing objects. 83 | */ 84 | enum DefragModes 85 | { 86 | /* \brief Will close holes immediately, eg the moment you release an object it will make sure to populate its place. */ 87 | DEFRAG_IMMEDIATE, 88 | 89 | /* \brief Will close holes next time we try to iterate the pool. the advantage here is that sometimes we release and 90 | then alloc, so in this case we won't do a useless moving, or if we release a huge chunk from the end. */ 91 | DEFRAG_DEFERRED, 92 | 93 | /* \brief Will never call defragging automatically, you need to call Defrag() yourself. */ 94 | DEFRAG_MANUAL, 95 | }; 96 | } -------------------------------------------------------------------------------- /dcm_pool/include/dcm_pool/object_in_pool.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file include\dcm_pool\object_in_pool.h. 3 | * 4 | * \brief Define the ObjectInPool template class. 5 | * This class represent an object inside the objects pool. 6 | * \license MIT 7 | * \autor Ronen Ness. 8 | * \since 2018 9 | */ 10 | 11 | #pragma once 12 | #include "defs.h" 13 | using namespace std; 14 | 15 | 16 | namespace dcm_pool 17 | { 18 | namespace _internal 19 | { 20 | 21 | /*! 22 | * \class ObjectInPool 23 | * 24 | * \brief A container that holds an object in the objects pool. 25 | * 26 | * \author Ronen 27 | * \date 2/21/2018 28 | * 29 | * \tparam T Base object type that you store in pool. 30 | */ 31 | template 32 | class ObjectInPool 33 | { 34 | private: 35 | 36 | /*! \brief The object itself. */ 37 | T _obj; 38 | 39 | /*! \brief Object id in objects pool. */ 40 | ObjectId _id; 41 | 42 | /*! \brief Is this object currently used? */ 43 | bool _is_used; 44 | 45 | public: 46 | 47 | /*! 48 | * \fn ObjectInPool::ObjectInPool(size_t index); 49 | * 50 | * \brief Constructor 51 | * 52 | * \author Ronen 53 | * \date 2/22/2018 54 | * 55 | * \param id Object unique id. 56 | */ 57 | ObjectInPool(ObjectId id = 0); 58 | 59 | /*! 60 | * \fn ObjectInPool::ObjectInPool(const ObjectInPool& other); 61 | * 62 | * \brief Constructor. 63 | * 64 | * \author Ronen 65 | * \date 2/23/2018 66 | * 67 | * \param other The other. 68 | */ 69 | ObjectInPool(const ObjectInPool& other); 70 | 71 | /*! 72 | * \fn V& ObjectInPool::operator=(ObjectInPool&& other); 73 | * 74 | * \brief Move assignment operator. 75 | * 76 | * \author Ronen 77 | * \date 2/23/2018 78 | * 79 | * \param [in,out] other The other. 80 | * 81 | * \return A shallow copy of this object. 82 | */ 83 | ObjectInPool& operator=(ObjectInPool&& other); 84 | 85 | /*! 86 | * \fn ObjectInPool& ObjectInPool::operator=(ObjectInPool& other); 87 | * 88 | * \brief Assignment operator. 89 | * 90 | * \author Ronen 91 | * \date 2/23/2018 92 | * 93 | * \param [in,out] other The other. 94 | * 95 | * \return A shallow copy of this object. 96 | */ 97 | ObjectInPool& operator=(const ObjectInPool& other); 98 | 99 | /*! 100 | * \fn inline ObjectId get_id() const; 101 | * 102 | * \brief Gets the object id. 103 | * 104 | * \author Ronen 105 | * \date 2/22/2018 106 | * 107 | * \return Return the object unique id. 108 | */ 109 | inline ObjectId get_id() const; 110 | 111 | /*! 112 | * \fn inline void set_id(ObjectId id); 113 | * 114 | * \brief Sets the object id. 115 | * 116 | * \author Ronen 117 | * \date 2/22/2018 118 | */ 119 | inline void set_id(ObjectId id); 120 | 121 | /*! 122 | * \fn inline bool ObjectInPool::is_used() const; 123 | * 124 | * \brief Query if this object is used 125 | * 126 | * \author Ronen 127 | * \date 2/22/2018 128 | * 129 | * \return True if used, false if not. 130 | */ 131 | inline bool is_used() const; 132 | 133 | /*! 134 | * \fn inline void ObjectInPool::set_is_used(); 135 | * 136 | * \brief Sets if the object is used. 137 | * 138 | * \author Ronen 139 | * \date 2/22/2018 140 | * 141 | * \param is_used Is the object used. 142 | */ 143 | inline void set_is_used(bool is_used); 144 | 145 | /*! 146 | * \fn inline T& ObjectInPool::get_object(); 147 | * 148 | * \brief Gets the object itself. 149 | * 150 | * \author Ronen 151 | * \date 2/22/2018 152 | * 153 | * \return The object. 154 | */ 155 | inline T& get_object(); 156 | }; 157 | } 158 | } 159 | 160 | // include implementation 161 | #include "_object_in_pool_imp.h" -------------------------------------------------------------------------------- /dcm_pool/include/dcm_pool/object_ptr.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file include\dcm_pool\object_ptr.h. 3 | * 4 | * \brief Define the ObjectPtr template class. 5 | * This class wraps a pointer to an object inside the objects pool. 6 | * \license MIT 7 | * \autor Ronen Ness. 8 | * \since 2018 9 | */ 10 | 11 | #pragma once 12 | #include "object_ptr.h" 13 | #include "object_in_pool.h" 14 | #include "defs.h" 15 | 16 | 17 | using namespace std; 18 | 19 | namespace dcm_pool 20 | { 21 | /*! 22 | * \class ObjectPtr 23 | * 24 | * \brief A pointer to an object inside the fast objects pool. 25 | * Use this for direct access to objects from the pool, or to release the object when 26 | * you're done using it. 27 | * 28 | * \author Ronen 29 | * \date 2/21/2018 30 | * 31 | * \tparam T Base object type that you store in pool. 32 | */ 33 | template 34 | class ObjectPtr 35 | { 36 | private: 37 | 38 | /*! \brief The pool containing this object. */ 39 | DcmPool* _pool; 40 | 41 | /*! \brief The object's unique id. */ 42 | ObjectId _id; 43 | 44 | /*! \brief Saving a cache of the actual object pointer. */ 45 | T* _cached_ptr; 46 | 47 | /*! \brief Last pool version to indicate if cached pointer is still valid to use. */ 48 | unsigned int _pool_defrag_version; 49 | 50 | public: 51 | 52 | /*! 53 | * \fn ObjectPtr::ObjectPtr(size_t index); 54 | * 55 | * \brief Constructor 56 | * 57 | * \author Ronen 58 | * \date 2/22/2018 59 | * 60 | * \param pool The parent objects pool. 61 | * \param id Object's unique id in pool. 62 | */ 63 | ObjectPtr(DcmPool* pool = NULL, ObjectId id = ObjectPoolMaxIndex); 64 | 65 | /*! 66 | * \fn inline ObjectId ObjectPtr::_get_id() const; 67 | * 68 | * \brief Gets the object id in pool. 69 | * 70 | * \author Ronen 71 | * \date 2/22/2018 72 | * 73 | * \return Return the object id. 74 | */ 75 | inline ObjectId _get_id() const; 76 | 77 | /*! 78 | * \fn T ObjectPtr::*operator*(void) const; 79 | * 80 | * \brief Return the object itself. 81 | * 82 | * \author Ronen 83 | * \date 2/22/2018 84 | * 85 | * \return The object instance. 86 | */ 87 | T& operator*(void); 88 | 89 | /*! 90 | * \fn T& ObjectPtr::operator->(void) const; 91 | * 92 | * \brief Member dereference operator. 93 | * 94 | * \author Ronen 95 | * \date 2/22/2018 96 | * 97 | * \return The dereferenced object. 98 | */ 99 | T* operator->(void); 100 | 101 | /*! 102 | * \fn inline bool ObjectPtr::operator==(const ObjectPtr& other) const 103 | * 104 | * \brief Equality operator. 105 | * 106 | * \author Ronen 107 | * \date 2/22/2018 108 | * 109 | * \param other The other pointer to compare to. 110 | * 111 | * \return True if the parameters are considered equivalent. 112 | */ 113 | inline bool operator==(const ObjectPtr& other) const { _id == other._id && _pool == other._pool; } 114 | 115 | /*! 116 | * \fn inline bool ObjectPtr::operator!=(const ObjectPtr& other) const 117 | * 118 | * \brief Inequality operator. 119 | * 120 | * \author Ronen 121 | * \date 2/22/2018 122 | * 123 | * \param other The other pointer to compare to. 124 | * 125 | * \return True if the parameters are not considered equivalent. 126 | */ 127 | inline bool operator!=(const ObjectPtr& other) const { return !(*this == other); } 128 | 129 | /*! 130 | * \fn inline void ObjectPtr::operator=(const ObjectPtr& other) 131 | * 132 | * \brief Assignment operator. 133 | * 134 | * \author Ronen 135 | * \date 2/22/2018 136 | * 137 | * \param other Other pointer to assign. 138 | */ 139 | 140 | inline void operator=(const ObjectPtr& other) { 141 | _id = other._id; 142 | _pool = other._pool; 143 | } 144 | 145 | /*! 146 | * \fn inline void ObjectPtr::_set_cached_ptr(T* ptr, unsigned int defrag_version) 147 | * 148 | * \brief Sets cached pointer and defrag version. 149 | * We use this to populate the pointer upon creation, since we already know the object address 150 | * at this point and its very likely that the user will want to use the pointer immediately after 151 | * getting it (to init the object). 152 | * 153 | * \author Ronen 154 | * \date 2/23/2018 155 | * 156 | * \param [in,out] ptr Object pointer. 157 | * \param defrag_version The pool's defrag version. 158 | */ 159 | inline void _set_cached_ptr(T* ptr, unsigned int defrag_version) { 160 | _cached_ptr = ptr; 161 | _pool_defrag_version = defrag_version; 162 | } 163 | }; 164 | } 165 | 166 | 167 | // include implementation 168 | #include "_object_ptr_imp.h" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /dcm_pool/include/dcm_pool/_dcm_pool_imp.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file include\dcm_pool\_object_pool_imp.h. 3 | * 4 | * \brief Implement the DcmPool template class. 5 | * \license MIT 6 | * \autor Ronen Ness. 7 | * \since 2018 8 | */ 9 | 10 | #include "exceptions.h" 11 | 12 | 13 | #ifndef __OBJECT_POOL_IMP__ 14 | #define __OBJECT_POOL_IMP__ 15 | 16 | 17 | namespace dcm_pool 18 | { 19 | template 20 | DcmPool::DcmPool(size_t max_size, size_t reserve, size_t shrink_threshold, DefragModes defrag_mode) : 21 | _max_size(max_size), 22 | _allocated_objects_count(0), 23 | _next_object_id(0), 24 | _max_used_index_in_vector(0), 25 | _shrink_pool_threshold(shrink_threshold), 26 | _defrag_mode(defrag_mode), 27 | _defrags_count(0), 28 | _holes(_objects) 29 | { 30 | // pre-alloc desired size 31 | if (reserve) 32 | { 33 | _objects.reserve(reserve); 34 | } 35 | } 36 | 37 | template 38 | typename DcmPool::Ptr DcmPool::Alloc() 39 | { 40 | // make sure didn't exceed pool limit 41 | if (_max_size && _allocated_objects_count >= _max_size) 42 | { 43 | throw ExceededPoolLimit(); 44 | } 45 | 46 | // will hole the index to allocate from 47 | std::size_t alloc_index; 48 | 49 | // do we have a hole to fill? if so, use it 50 | if (_holes.size()) 51 | { 52 | // get index to alloc on and remove from holes vector 53 | alloc_index = _holes.pop_back(); 54 | 55 | // return the new object pointer 56 | return AssignObject(alloc_index); 57 | } 58 | 59 | // do we have unused objects at the end of the vector? fill them 60 | if (_max_used_index_in_vector + 1 < _objects.size()) 61 | { 62 | return AssignObject(_max_used_index_in_vector + 1); 63 | } 64 | 65 | // if we got here it means we don't have any hole to fill, and must allocate a new object in pool's vector 66 | auto index = _objects.size(); 67 | _objects.push_back(_internal::ObjectInPool()); 68 | return AssignObject(index); 69 | } 70 | 71 | template 72 | typename DcmPool::Ptr DcmPool::DcmPool::AssignObject(size_t index) 73 | { 74 | // increase allocated objects count 75 | _allocated_objects_count++; 76 | 77 | // update max used index, if needed 78 | if (index > _max_used_index_in_vector) 79 | { 80 | _max_used_index_in_vector = index; 81 | } 82 | 83 | // get object and id, set it as used 84 | _internal::ObjectInPool& obj = _objects[index]; 85 | ObjectId id = _next_object_id++; 86 | obj.set_id(id); 87 | obj.set_is_used(true); 88 | 89 | // update the pointers table and create a pointer to return 90 | _pointers[id] = index; 91 | auto ret = DcmPool::Ptr(this, id); 92 | ret._set_cached_ptr(&obj.get_object(), _defrags_count); 93 | 94 | // if defined, call the OnAlloc event handler 95 | if (OnAlloc) OnAlloc(obj.get_object(), id, *this); 96 | 97 | // return the object 98 | return ret; 99 | } 100 | 101 | template 102 | T& DcmPool::_get_object(ObjectId id) 103 | { 104 | size_t index = _pointers.at(id); 105 | _internal::ObjectInPool& obj = *(&_objects[index]); 106 | return obj.get_object(); 107 | } 108 | 109 | template 110 | void DcmPool::Release(typename DcmPool::Ptr obj) 111 | { 112 | Release(obj._get_id()); 113 | } 114 | 115 | template 116 | void DcmPool::Release(ObjectId id) 117 | { 118 | // get object index in pool and a reference to the object itself 119 | auto index = _pointers[id]; 120 | _internal::ObjectInPool& obj_ref = _objects[index]; 121 | 122 | // sanity test 123 | if (!obj_ref.is_used()) 124 | { 125 | throw AccessViolation(); 126 | } 127 | 128 | // if defined, call the OnRelease event handler 129 | if (OnRelease) OnRelease(obj_ref.get_object(), id, *this); 130 | 131 | // first, remove object from pointers map 132 | _pointers.erase(id); 133 | 134 | // now decrease actual pool size 135 | _allocated_objects_count--; 136 | 137 | // set as no longer used 138 | obj_ref.set_is_used(false); 139 | 140 | // if we happened to release the last object in pool, its the easier case - we just decrease the used index as well 141 | if (index == _max_used_index_in_vector) 142 | { 143 | _max_used_index_in_vector--; 144 | return; 145 | } 146 | 147 | // if got here it means we created a hole. add it to holes vector 148 | _holes.push_back(index); 149 | 150 | // if in immediate defrag mode, do it now 151 | if (_defrag_mode == DEFRAG_IMMEDIATE) 152 | { 153 | Defrag(); 154 | } 155 | } 156 | 157 | template 158 | size_t DcmPool::size() const 159 | { 160 | return _allocated_objects_count; 161 | } 162 | 163 | template 164 | void DcmPool::Clear() 165 | { 166 | _pointers.clear(); 167 | _objects.clear(); 168 | _holes.clear(); 169 | _allocated_objects_count = 0; 170 | _next_object_id = 0; 171 | _max_used_index_in_vector = 0; 172 | } 173 | 174 | template 175 | void DcmPool::Defrag() 176 | { 177 | // no holes to fill? nothing to do here 178 | if (!_holes.size()) 179 | { 180 | return; 181 | } 182 | 183 | // increase defragging count 184 | _defrags_count++; 185 | 186 | // iterate and close holes until we no longer have holes to close 187 | while (_holes.size()) 188 | { 189 | // get current index to move 190 | auto index_to_fill = _holes.pop_back(); 191 | 192 | // if index to fill is outside max used index, skip 193 | if (index_to_fill >= _max_used_index_in_vector) 194 | { 195 | continue; 196 | } 197 | 198 | // move last object into this position 199 | _objects[index_to_fill] = std::move(_objects[_max_used_index_in_vector]); 200 | 201 | // update max used index in vector 202 | do 203 | { 204 | _max_used_index_in_vector--; 205 | } 206 | while (_max_used_index_in_vector > 0 && !_objects[_max_used_index_in_vector].is_used()); 207 | 208 | // update the pointers table 209 | _pointers[_objects[index_to_fill].get_id()] = index_to_fill; 210 | } 211 | 212 | // check if we need to resize vector 213 | if (_objects.size() - _max_used_index_in_vector > _shrink_pool_threshold) 214 | { 215 | ClearUnusedMemory(); 216 | } 217 | } 218 | 219 | template 220 | void DcmPool::Reserve(size_t amount) 221 | { 222 | _objects.reserve(amount); 223 | } 224 | 225 | template 226 | void DcmPool::IterateEx(PoolIteratorEx callback) 227 | { 228 | // if in deferred defrag mode, do it now 229 | if (_defrag_mode == DEFRAG_DEFERRED) 230 | { 231 | Defrag(); 232 | } 233 | 234 | // iterate objects 235 | for (size_t i = 0; i <= _max_used_index_in_vector; ++i) 236 | { 237 | _internal::ObjectInPool& obj = _objects[i]; 238 | if (obj.is_used()) 239 | { 240 | if (callback(obj.get_object(), obj.get_id(), *this) == IterationReturnCode::ITER_BREAK) 241 | break; 242 | } 243 | } 244 | } 245 | 246 | template 247 | void DcmPool::Iterate(PoolIterator callback) 248 | { 249 | // if in deferred defrag mode, do it now 250 | if (_defrag_mode == DEFRAG_DEFERRED) 251 | { 252 | Defrag(); 253 | } 254 | 255 | // iterate objects 256 | for (size_t i = 0; i <= _max_used_index_in_vector; ++i) 257 | { 258 | _internal::ObjectInPool& obj = _objects[i]; 259 | if (obj.is_used()) 260 | { 261 | callback(obj.get_object(), obj.get_id()); 262 | } 263 | } 264 | } 265 | 266 | template 267 | void DcmPool::IterateEx(ConstPoolIteratorEx callback) const 268 | { 269 | // iterate objects 270 | for (size_t i = 0; i <= _max_used_index_in_vector; ++i) 271 | { 272 | _internal::ObjectInPool& obj = _objects[i]; 273 | if (obj.is_used()) 274 | { 275 | if (callback(obj.get_object(), obj.get_id(), *this) == IterationReturnCode::ITER_BREAK) 276 | break; 277 | } 278 | } 279 | } 280 | 281 | template 282 | void DcmPool::Iterate(ConstPoolIterator callback) const 283 | { 284 | // iterate objects 285 | for (size_t i = 0; i <= _max_used_index_in_vector; ++i) 286 | { 287 | _internal::ObjectInPool& obj = _objects[i]; 288 | if (obj.is_used()) 289 | { 290 | callback(obj.get_object(), obj.get_id()); 291 | } 292 | } 293 | } 294 | 295 | template 296 | void DcmPool::ClearUnusedMemory() 297 | { 298 | // make sure there are not holes 299 | if (_holes.size()) 300 | { 301 | throw CannotResizeWhileNotDefragged(); 302 | } 303 | 304 | // resize objects pool 305 | _objects.resize(_max_used_index_in_vector + 1); 306 | } 307 | } 308 | 309 | #endif // !__OBJECT_POOL_IMP__ -------------------------------------------------------------------------------- /dcm_pool/dcm_pool.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {10589364-1700-46A8-808E-5A31187B1FC7} 24 | Win32Proj 25 | FastObjectsPool 26 | 10.0.16299.0 27 | dcm_pool 28 | 29 | 30 | 31 | Application 32 | true 33 | v141 34 | Unicode 35 | 36 | 37 | Application 38 | false 39 | v141 40 | true 41 | Unicode 42 | 43 | 44 | Application 45 | true 46 | v141 47 | Unicode 48 | 49 | 50 | Application 51 | false 52 | v141 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | $(ProjectDir);$(IncludePath) 77 | 78 | 79 | true 80 | $(ProjectDir);$(IncludePath) 81 | 82 | 83 | false 84 | $(ProjectDir);$(IncludePath) 85 | 86 | 87 | false 88 | $(ProjectDir);$(IncludePath) 89 | 90 | 91 | 92 | NotUsing 93 | Level3 94 | Disabled 95 | true 96 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 97 | true 98 | true 99 | 100 | 101 | Console 102 | true 103 | 104 | 105 | 106 | 107 | NotUsing 108 | Level3 109 | Disabled 110 | true 111 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 112 | true 113 | true 114 | 115 | 116 | Console 117 | true 118 | 119 | 120 | 121 | 122 | NotUsing 123 | Level3 124 | MaxSpeed 125 | true 126 | true 127 | true 128 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 129 | true 130 | true 131 | 132 | 133 | Console 134 | true 135 | true 136 | true 137 | 138 | 139 | 140 | 141 | NotUsing 142 | Level3 143 | MaxSpeed 144 | true 145 | true 146 | true 147 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 148 | true 149 | true 150 | 151 | 152 | Console 153 | true 154 | true 155 | true 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /dcm_pool/include/dcm_pool/dcm_pool.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file include\dcm_pool\objects_pool.h. 3 | * 4 | * \brief Define the main DcmPool template class. 5 | * \license MIT 6 | * \autor Ronen Ness. 7 | * \since 2018 8 | */ 9 | 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include "object_ptr.h" 16 | #include "holes_list.h" 17 | #include "defs.h" 18 | 19 | using namespace std; 20 | 21 | namespace dcm_pool 22 | { 23 | /*! 24 | * \class DcmPool 25 | * 26 | * \brief Dynamic, Contiguous-Memory objects pool. 27 | * This object allows you to quickly allocate and release objects from a dynamic pool, without unnecessary new/release calls. 28 | * In addition, it keeps all objects in a single block of continuous memory, so iterating them is ultra-fast and benefits greatly 29 | * from CPU-caching of RAM blocks. 30 | * 31 | * In short, this pool is.. 32 | * - Dynamic: does not pre-allocate all objects. It grows and shrink as you use it. 33 | * - Contiguous: will hold all objects in a contiguous memory block, and defrag when needed. 34 | * 35 | * Notes: 36 | * - To access an object from the pool externally (eg not via iteration) you need to use the DcmPool::Ptr object. 37 | * - The pool use the move assignment operator internally, so implementing it will boost performance greatly. 38 | * - Your objects (T) must have a default constructor, and obviously an Init() function you'll need to call manually after allocating. 39 | * - The pool is not thread safe! 40 | * 41 | * Performance: 42 | * - Iterating objects in the pool is optimal, eg O(N) on a contiguous memory block. 43 | * - Allocting is normally O(1) (unless exceed memory block and need to realloc the whole pool - can be avoided with reserved). 44 | * - Releasing is normally O(1). 45 | * - Accessing from the object pointer is ~O(1), sometimes will invoke accessing unordered map. 46 | * 47 | * Defragging: 48 | * To keep the memory Contiguous, there's a need to 'close holes' whenever they are created, eg when an object is 49 | * released from the pool (and its index is not the last index). There are 3 modes to handle defragging: 50 | * - DEFRAG_IMMEDIATE: will close holes the moment they are created. This option is not optimal but have predictable speed. 51 | * - DEFRAG_DEFERRED: will do defragging when trying to iterate the pool. More efficient, but less predictable. 52 | * - DEFRAG_MANUAL: will not do defragging automatically, you need to call Defrag() yourself when you see fit. 53 | * 54 | * Usecase: 55 | * This pool is useful for scenarios where you need to do a lot of allocating and releasing of objects, while 56 | * having an update loop that needs to process all of them on a timely-basis. 57 | * For example, think of a game with pool of bullets - whenever an enemy shoots a bullet we need to allocate an 58 | * object, when it hits we need to release it, and every frame we need to iterate and move all bullets. 59 | * This pool help us manage those bullets pool in a smart way that iterating them is easy, and we don't really 60 | * allocate and release them all the time. 61 | * 62 | * How does it work: 63 | * 1. The pool uses a vector internally to store the objects in memory. 64 | * 2. The vector grows and shrink as the pool changes its size. 65 | * 3. When you release an object, it creates a 'hole' in the contiguous vector memory. 66 | * 3. a. That hole is closed during the defragging process. 67 | * 3. b. We can accumulate a list of holes if defragging mode is not immediate. 68 | * 3. c. Defragging takes O(N), where N is number of holes (not number of pool). We close holes by taking objects from the end of the vector. 69 | * 4. To iterate the vector you call Iterate(), which takes a function pointer to run on every valid object in pool. 70 | * 5. Since defragging shuffles memory, you can't access objects by index. To solve this, we use a special pointer class to allow access to specific objects: 71 | * 5. a. Every object in pool is asigned with a unique id. 72 | * 5. b. The pool keeps an internal hash table to convert id to actual index (hidden from the user). 73 | * 5. c. When the pointer tries to fetch the object it points on, if the pool was defragged since last access it use the hash table to find the new objects index. 74 | * 6. To store the list of holes in the vector we reuse the free objects, so no additional memory is wasted. 75 | * 76 | * 77 | * \author Ronen 78 | * \date 2/21/2018 79 | * 80 | * \tparam T Type of objects to place in pool. 81 | * Note: to fully enjoy the benefit of continuous memory and cpu caching, its recommended to 82 | * provide an actual type and not a pointer. 83 | */ 84 | template 85 | class DcmPool 86 | { 87 | public: 88 | 89 | /*! 90 | * \class Ptr 91 | * 92 | * \brief A pointer to an object inside this pool. 93 | * 94 | * \author Ronen 95 | * \date 2/23/2018 96 | */ 97 | class Ptr : public ObjectPtr 98 | { 99 | public: 100 | Ptr(DcmPool* pool = NULL, ObjectId id = ObjectPoolMaxIndex) : 101 | ObjectPtr(pool, id) {} 102 | }; 103 | 104 | /*! \brief Callback to invoke on every new object you allocate. */ 105 | EventsHandler OnAlloc = NULL; 106 | 107 | /*! \brief Callback to invoke on every object you release. */ 108 | EventsHandler OnRelease = NULL; 109 | 110 | private: 111 | 112 | /*! \brief The pooled objects. */ 113 | vector<_internal::ObjectInPool > _objects; 114 | 115 | /*! \brief Convert unique object id to its index in pools vector. */ 116 | unordered_map _pointers; 117 | 118 | // holes inside the pool 119 | _internal::HolesList _holes; 120 | 121 | /*! \brief Used to assign unique object ids to new objects in pool. */ 122 | ObjectId _next_object_id; 123 | 124 | /*! \brief Max objects count in pool. */ 125 | size_t _max_size; 126 | 127 | /*! \brief Current pool actual size (allocated objects). */ 128 | size_t _allocated_objects_count; 129 | 130 | /*! \brief Highest index we actually use in the objects vector (remember that there could be holes in the vector if not defragged). */ 131 | size_t _max_used_index_in_vector; 132 | 133 | /*! \brief Required diff between actually used objects and vector size to make us resize the vector. */ 134 | size_t _shrink_pool_threshold; 135 | 136 | /*! \brief How to handle defragging. */ 137 | DefragModes _defrag_mode; 138 | 139 | /*! \brief How many times was this pool defragged? */ 140 | unsigned int _defrags_count; 141 | 142 | public: 143 | 144 | /*! 145 | * \fn DcmPool::DcmPool(std::size_t max_size); 146 | * 147 | * \brief Constructor 148 | * 149 | * \author Ronen 150 | * \date 2/21/2018 151 | * 152 | * \param max_size Maximum objects count in pool. Set to 0 for unlimited count. 153 | * \param reserve How many objects to reserve in the pool memory (using vector's reserve). 154 | * \param shrink_threshold The pool uses a vector internally to hold objects. When you allocate more objects, the vector grows. 155 | * This number decides when to shrink the vector down, if objects are released. 156 | * \param defrag_mode How to handle defragging. 157 | */ 158 | DcmPool(size_t max_size = 0, size_t reserve = 0, size_t shrink_threshold = 1024, DefragModes defrag_mode = DEFRAG_DEFERRED); 159 | 160 | /*! 161 | * \fn Ptr DcmPool::Alloc(); 162 | * 163 | * \brief Allocate an object from the pool. 164 | * 165 | * \author Ronen 166 | * \date 2/21/2018 167 | * 168 | * \return An ObjectPtr pointing at the newly-allocated object. 169 | * You must keep it to later release the object. 170 | */ 171 | Ptr Alloc(); 172 | 173 | /*! 174 | * \fn void DcmPool::Release(ObjectPtr obj); 175 | * 176 | * \brief Releases the given object and return it to the pool. 177 | * 178 | * \author Ronen 179 | * \date 2/21/2018 180 | * 181 | * \param obj Object to release. 182 | */ 183 | void Release(Ptr obj); 184 | 185 | /*! 186 | * \fn void DcmPool::Release(ObjectId id); 187 | * 188 | * \brief Releases the given object and return it to the pool. 189 | * 190 | * \author Ronen 191 | * \date 2/21/2018 192 | * 193 | * \param id Object to release. 194 | */ 195 | void Release(ObjectId id); 196 | 197 | /*! 198 | * \fn void DcmPool::Reserve(size_t amount); 199 | * 200 | * \brief Reserves the given amount in the internal vector used to hold the pool. 201 | * 202 | * \author Ronen 203 | * \date 3/8/2018 204 | * 205 | * \param amount The amount of objects to reserve. 206 | */ 207 | void Reserve(size_t amount); 208 | 209 | /*! 210 | * \fn void DcmPool::Iterate(PoolIterator callback); 211 | * 212 | * \brief Iterates all the objects in pool. 213 | * Note: if working in deferred defrag mode, this will trigger defrag. 214 | * 215 | * \author Ronen 216 | * \date 2/22/2018 217 | * 218 | * \param callback The callback to use on the objects while iterating. 219 | * Return false to break the iteration. 220 | */ 221 | void Iterate(PoolIterator callback); 222 | 223 | /*! 224 | * \fn void DcmPool::Iterate(PoolIteratorEx callback); 225 | * 226 | * \brief Iterates all the objects in pool with extended options. 227 | * Note: if working in deferred defrag mode, this will trigger defrag. 228 | * 229 | * \author Ronen 230 | * \date 2/22/2018 231 | * 232 | * \param callback The callback to use on the objects while iterating. 233 | * Return false to break the iteration. 234 | */ 235 | void IterateEx(PoolIteratorEx callback); 236 | 237 | /*! 238 | * \fn void DcmPool::Iterate(ConstPoolIterator callback) const; 239 | * 240 | * \brief Iterates all the objects in pool. 241 | * Note: if working in deferred defrag mode, this will trigger defrag. 242 | * 243 | * \author Ronen 244 | * \date 2/22/2018 245 | * 246 | * \param callback The callback to use on the objects while iterating. 247 | * Return false to break the iteration. 248 | */ 249 | void Iterate(ConstPoolIterator callback) const; 250 | 251 | /*! 252 | * \fn void DcmPool::Iterate(ConstPoolIteratorEx callback) const; 253 | * 254 | * \brief Iterates all the objects in pool with extended options. 255 | * Note: if working in deferred defrag mode, this will trigger defrag. 256 | * 257 | * \author Ronen 258 | * \date 2/22/2018 259 | * 260 | * \param callback The callback to use on the objects while iterating. 261 | * Return false to break the iteration. 262 | */ 263 | void IterateEx(ConstPoolIteratorEx callback) const; 264 | 265 | /*! 266 | * \fn void DcmPool::Clear(); 267 | * 268 | * \brief Clears the entire pool, making all objects in it free. 269 | * Watch out, don't use this if you still hold pointer to objects from outside. 270 | * 271 | * \author Ronen 272 | * \date 2/22/2018 273 | */ 274 | void Clear(); 275 | 276 | /*! 277 | * \fn inline size_t DcmPool::get_size() const; 278 | * 279 | * \brief Gets the size of the pool, eg allocated objects count. 280 | * 281 | * \author Ronen 282 | * \date 2/22/2018 283 | * 284 | * \return Pool size. 285 | */ 286 | inline size_t size() const; 287 | 288 | /*! 289 | * \fn T DcmPool::_get_object(ObjectId id); 290 | * 291 | * \brief Gets an object from id. 292 | * 293 | * \author Ronen 294 | * \date 2/22/2018 295 | * 296 | * \param id Object id to get.. 297 | * 298 | * \return The object itself. 299 | */ 300 | T& _get_object(ObjectId id); 301 | 302 | /*! 303 | * \fn void DcmPool::ClearUnusedMemory(); 304 | * 305 | * \brief Force the pool to clear unused memory now. 306 | * This process happens automatically as you release objects, based on shrink_threshold. 307 | * 308 | * \author Ronen 309 | * \date 2/22/2018 310 | */ 311 | void ClearUnusedMemory(); 312 | 313 | /*! 314 | * \fn void DcmPool::Defrag(); 315 | * 316 | * \brief Defrags the pool to make the memory continuous. 317 | * 318 | * \author Ronen 319 | * \date 2/22/2018 320 | */ 321 | void Defrag(); 322 | 323 | /*! 324 | * \fn inline unsigned int DcmPool::_get_defrags_count() const 325 | * 326 | * \brief Gets defrags count. 327 | * 328 | * \author Ronen 329 | * \date 2/22/2018 330 | * 331 | * \return The defrags count. 332 | */ 333 | inline unsigned int _get_defrags_count() const { return _defrags_count; } 334 | 335 | private: 336 | 337 | /*! 338 | * \fn Ptr DcmPool::AssignObject(size_t index); 339 | * 340 | * \brief Assign an object in the pool (internally) and return its object pointer. 341 | * 342 | * \author Ronen 343 | * \date 2/22/2018 344 | * 345 | * \param index Zero-based index of the object in pool. 346 | * 347 | * \return New object pointer. 348 | */ 349 | Ptr AssignObject(size_t index); 350 | 351 | }; 352 | } 353 | 354 | // include implementation 355 | #include "_dcm_pool_imp.h" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dcm_pool 2 | Dynamic, Contiguous-Memory Objects Pool. 3 | 4 | ## What is it 5 | 6 | A dynamic contiguous-memory Pool, or in short a dcm_pool, is a pool of objects that guarantee the following: 7 | 8 | 1. All objects are kept in memory in a contiguous memory block, and iterating them is equivalent to iterating a vector. 9 | 2. Accessing objects by pointer is O(1). 10 | 3. Allocating new objects is O(1), and does not cause an actual 'new()' call every time. 11 | 4. Releasing an object is O(1), and does not cause an actual 'delete()' call every time. 12 | 5. The pool can be dynamic, you don't have to set a hard size limit or allocate up-front. 13 | 14 | ## Why 15 | 16 | This pool have very specific use cases, so best way to describe it is by example. 17 | 18 | Lets say you're building a game engine with the [Entity-Component-System](https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system) pattern, which is very common with video game engines. In ECS design you have a basic 'entity' object, to which you attach different 'components' that implement different features and behaviours. 19 | 20 | Now you have different type of components attached to different entities, and they all require an 'Update()' call every frame. You have no way to tell how many components you'll need, and you probably want to pool your components so that repeated assigning and releasing won't cause too many memory management calls. In addition, you want similar components to be in contiguous memory, to enjoy [memory access optimizations](https://bitbashing.io/memory-performance.html). 21 | 22 | If you use a regular pool, you can easily reduce the new() and delete() calls, but your objects memory will not be contiguous and iterating them for the Update() loop would be less efficient. If you use a vector or other contiguous-memory container you'll enjoy a very fast iteration, but slow releasing / allocating (even if you use reserve - releasing might cause a shift of memory). In addition, using vector will not allow you to hold pointers or indexes to objects inside the pool, as those could change. 23 | 24 | This lib provides a pool to answer just that: reduce new / delete calls, keep everything in contiguous memory, and allow direct access using pointers. 25 | 26 | ## Install 27 | 28 | dcm_pool is a header-only lib, you don't need to compile any .lib or .dll files. 29 | 30 | To use it, simply get the header files from [dcm_pool/include/](https://github.com/RonenNess/dcm_pool/tree/master/dcm_pool/include/) and make them available to your compiler. 31 | 32 | ## Usage 33 | 34 | First lets take a look at a full, simple example: 35 | 36 | ```cpp 37 | #include 38 | using namespace dcm_pool; 39 | 40 | // a dog class - the objects we want to pool 41 | class Dog 42 | { 43 | public: 44 | 45 | // what dogs do every frame 46 | void Update() 47 | { 48 | cout << "Woof!" << endl; 49 | } 50 | 51 | // rub the dog's belly 52 | void RubBelly() 53 | { 54 | cout << "Your happiness increased by 5%." << endl; 55 | } 56 | }; 57 | 58 | // main 59 | void main() 60 | { 61 | // create a pool of dogs 62 | DcmPool pool; 63 | 64 | // alloc a new dog and rub its belly 65 | auto new_dog = pool.Alloc(); 66 | new_dog->RubBelly(); 67 | 68 | // create another dog 69 | auto new_dog2 = pool.Alloc(); 70 | 71 | // release the first dog 72 | pool.Release(new_dog); 73 | 74 | // iterate over all dogs in pool and update (using lambda) 75 | pool.Iterate([](Dog& dog, ObjectId id) { dog.Update(); }); 76 | } 77 | ``` 78 | 79 | Now lets dive into more details: 80 | 81 | 82 | ### Creating A Pool 83 | 84 | Creating a new pool is easy: 85 | 86 | ```cpp 87 | DcmPool pool; 88 | ``` 89 | 90 | Note that its best to use the object type itself, and not a reference or a pointer. If you use pointers you'll lose some of the memory-access performance. 91 | 92 | The objects pool constructor receive several optional params to help you fine-tune its behaviour: 93 | 94 | ```cpp 95 | DcmPool(size_t max_size = 0, size_t reserve = 0, size_t shrink_threshold = 1024, DefragModes defrag_mode = DEFRAG_DEFERRED); 96 | ``` 97 | 98 | - **max_size**: If provided, will limit the pool size (throw exception if exceed limit). 99 | - **reserve**: If provided, will reserve this amount of objects capacity in internal vector. 100 | - **shrink_threshold**: While the pool grows dynamically, we only shrink the pool's memory chunk when having this amount of free objects in pool. 101 | - **defrag_mode**: When to handle defragging - immediately on release, when trying to iterate objects, or manually. 102 | 103 | You can understand from the params above that if you want a constant-size pool you can set `reserve` and `max_size` to the same value, and you'll have 0 new() / delete() calls. 104 | 105 | ### Allocating & Releasing 106 | 107 | To allocate a new object from pool you need to use ```Alloc```: 108 | 109 | ```cpp 110 | auto newobj = pool.Alloc(); 111 | ``` 112 | 113 | Note that this will not invoke the object's constructor. If you want an initialize function you need to define one and call it manually. 114 | 115 | The returned value of ```Alloc``` is a pointer-like object that provide a direct access to the object in pool. Even after defragging, the pointer will not lose its reference (but never try to grab the actual address of the object as it might change internally). 116 | 117 | Accessing the pointer directly is not as efficient as using a regular pointer and may require a single hash-table lookup, if the pool was defragged since you last used it. 118 | 119 | When you're done with the object and want to release it, use ```Release```: 120 | 121 | ```cpp 122 | pool.Release(newobj); 123 | ``` 124 | 125 | Similar to with ```Alloc```, this will not call the object destructor. 126 | 127 | ### Iterating Pool 128 | 129 | The main way to iterate a pool is via the ```Iterate``` function. With a lambda, it looks like this: 130 | 131 | ```cpp 132 | pool.Iterate([](MyObjectType& obj, ObjectId id) { /* do something with object */ }); 133 | ``` 134 | 135 | Note the extra param, ObjectId. This id represent the unique, constant id of the object in pool. You can use this id to create new object pointers or release the object using id instead of pointer. 136 | 137 | If you want to use a regular function and not a lambda, the example above would look like this: 138 | 139 | ```cpp 140 | // iteration callback 141 | void update_loop(MyObjectType& obj, ObjectId id) 142 | { 143 | // your update code here 144 | } 145 | 146 | // using the iteration callback: 147 | pool.Iterate(update_loop); 148 | ``` 149 | 150 | Or if you need more control, you can use the ```IterateEx``` version: 151 | 152 | ```cpp 153 | // iteration callback 154 | IterationReturnCode update_loop(MyObjectType& obj, ObjectId id, DcmPool& pool) 155 | { 156 | // your update code here 157 | 158 | // continue to next object 159 | return IterationReturnCode::ITER_CONTINUE; 160 | } 161 | 162 | // using the iteration callback: 163 | pool.IterateEx(update_loop); 164 | ``` 165 | 166 | Note that for const pools you have a corresponding iteration function that receive a similar signature but with const references. 167 | 168 | ### Handling Init / Terminate Automatically 169 | 170 | As mentioned before, calling ```Alloc``` and ```Release``` does not guarantee corresponding constructor / destructor calls. So you can't count on ctor / dtor with your objects. 171 | 172 | However, dcm_pool provide a simple way to automatically invoke a custom Init / Terminate function whenever you ```Alloc``` or ```Release``` an object: 173 | 174 | ```cpp 175 | // call obj.Init() whenever a new object is allocated 176 | pool.OnAlloc = [](MyObjectType& obj, ObjectId id, DcmPool& pool) { 177 | obj.Init(); 178 | }; 179 | 180 | // call obj.Release() whenever an object is being released 181 | pool.OnRelease = [](MyObjectType& obj, ObjectId id, DcmPool& pool) { 182 | obj.Release(); 183 | }; 184 | ``` 185 | 186 | Just like you would normally avoid raising exceptions from a normal constructor / destructor, you should avoid raising exceptions from your Init / Terminate as well, as it could cause undefined behavior with the pool. 187 | 188 | ### Cleanup 189 | 190 | The pool will shrink its memory chunk automatically when there are too many unused objects in pool. 191 | However, if you want to reduce the pool's size immediately, you can call: 192 | 193 | ```cpp 194 | pool.ClearUnusedMemory(); 195 | ``` 196 | 197 | Note that if the pool is not defragged (eg have holes in it) it will raise exception. 198 | 199 | ### Defragging 200 | 201 | As mentioned before, the pool might have "holes" in its contiguous memory due to objects being released from the middle. To solve this, the dcm_pool do self-defragging. 202 | 203 | The defragging process worst case takes O(N), where N is number of holes in the pool and not number of objects. 204 | 205 | dcm_pool Support 3 defragging modes: 206 | 207 | #### DEFRAG_IMMEDIATE 208 | 209 | Will close the hole immediately as its created. With this mode the performance is deterministic; The moment you release an object that creates a hole, it will be closed by moving the last object in its place. 210 | 211 | #### DEFRAG_DEFERRED 212 | 213 | In this mode the pool will accumulate holes and only close them when trying to iterate the pool. The huge advantage here is that if you do something like Release -> Alloc -> Release -> Alloc, in the two times you alloc it will just return the unused objects in the middle, and no will defragging will happen. 214 | 215 | The disadvantage is that the performance of the Iteration() call becomes non-deterministic and may change depending on how many holes you have in your contiguous memory. 216 | 217 | #### DEFRAG_MANUAL 218 | 219 | In this mode the pool will never defrag on its own. If you iterate a pool with holes it will just skip the unused objects, and you'll need to call ```pool.Defrag()``` manually when you think its right. 220 | 221 | ## Limitations & Tips 222 | 223 | 1. The objects you use in pool must have a default constructor. 224 | 2. Allocating new objects will not invoke a constructor call. You need to create and call an Init() function manually. 225 | 3. Releasing objects will not invoke a destructor call. You need to create and call a Dtor() function manually. 226 | 4. As an extension of the constructor / destructor limitation, if you do implement them they shouldn't do any heavy lifting as they may be called internally. 227 | 5. Implementing a Move Assignment Operator will increase performance significantly. 228 | 6. To maximize the memory-based optimization, don't use the pool to store pointers or references. 229 | 230 | As you can see the limitations above apply to most basic pooling solutions. 231 | 232 | ## How does it work 233 | 234 | 1. The pool uses a vector internally to store the objects in memory. 235 | 2. The vector grows and shrink as the pool changes its size. 236 | 3. When you release an object, it creates a 'hole' in the contiguous vector memory. 237 | 3. a. That hole is closed during the defragging process. 238 | 3. b. We can accumulate a list of holes if defragging mode is not immediate. 239 | 3. c. Defragging takes O(N), where N is number of holes (not number of pool). We close holes by taking objects from the end of the vector. 240 | 4. To iterate the vector you call Iterate(), which takes a function pointer to run on every valid object in pool. 241 | 5. Since defragging shuffles memory, you can't access objects by index. To solve this, we use a special pointer class to allow access to specific objects: 242 | 5. a. Every object in pool is asigned with a unique id. 243 | 5. b. The pool keeps an internal hash table to convert id to actual index (hidden from the user). 244 | 5. c. When the pointer tries to fetch the object it points on, if the pool was defragged since last access it use the hash table to find the new objects index. 245 | 6. To store the list of holes in the vector we reuse the free objects, so no additional memory is wasted. 246 | 247 | ## Memory Consumption 248 | 249 | In addition to the objects themselves, dcm_pool adds additional unique id per object (an int) + a hash table to convert id to index. 250 | 251 | ## Performance 252 | 253 | To test the dcm_pool I created a simple object type that has an hp counter and an Update() function that randomly decreases it. When reaching 0, the object is dead and is removed. 254 | 255 | I simulate few 'Update' loops, where in every loop I create new 15000 objects, add them to pool, and iterate all the existing objects in pool to Update them. Then I measure how long it took to update everything, adding the new objects, and deleting the dead ones. 256 | 257 | I compared the 'dcm_pool' performance to a simple list and a vector naive implementation. 258 | 259 | The test is not very 'fair' because the list and vector are used in a very naive way, but it still gives a general idea about how efficient 'dcm_pool' is. 260 | 261 | Note: Although possible, I didn't allocate memory upfront for the pool nor the vector. 262 | 263 | The results are below: 264 | 265 | ``` 266 | ========================== 267 | TEST DCM_POOL 268 | ========================== 269 | 270 | Frame: 0 271 | Loops per frame: 15000 272 | Pool current size: 1 273 | Total update calls: 0 274 | Iterations total time: 0 275 | Allocations total time: 0 276 | Remove objects total time: 0 277 | Objects removed this frame: 0 278 | Objects added this frame: 1 279 | -------------------------- 280 | Frame: 1 281 | Loops per frame: 15000 282 | Pool current size: 6950 283 | Total update calls: 72430695 284 | Iterations total time: 23.246 285 | Allocations total time: 0.155 286 | Remove objects total time: 0.159 287 | Objects removed this frame: 8051 288 | Objects added this frame: 15000 289 | -------------------------- 290 | Frame: 2 291 | Loops per frame: 15000 292 | Pool current size: 6970 293 | Total update calls: 104489064 294 | Iterations total time: 33.579 295 | Allocations total time: 0.286 296 | Remove objects total time: 0.277 297 | Objects removed this frame: 14980 298 | Objects added this frame: 15000 299 | -------------------------- 300 | Frame: 3 301 | Loops per frame: 15000 302 | Pool current size: 6950 303 | Total update calls: 104463885 304 | Iterations total time: 33.439 305 | Allocations total time: 0.406 306 | Remove objects total time: 0.28 307 | Objects removed this frame: 15020 308 | Objects added this frame: 15000 309 | -------------------------- 310 | Frame: 4 311 | Loops per frame: 15000 312 | Pool current size: 6831 313 | Total update calls: 104115381 314 | Iterations total time: 33.994 315 | Allocations total time: 0.531 316 | Remove objects total time: 0.262 317 | Objects removed this frame: 15119 318 | Objects added this frame: 15000 319 | -------------------------- 320 | 321 | 322 | ========================== 323 | TEST LIST 324 | ========================== 325 | 326 | Frame: 0 327 | Loops per frame: 15000 328 | Pool current size: 1 329 | Total update calls: 1 330 | Iterations total time: 0 331 | Allocations total time: 0 332 | Remove objects total time: 0 333 | Objects removed this frame: 0 334 | Objects added this frame: 1 335 | -------------------------- 336 | Frame: 1 337 | Loops per frame: 15000 338 | Pool current size: 6976 339 | Total update calls: 72635680 340 | Iterations total time: 51.905 341 | Allocations total time: 0.023 342 | Remove objects total time: 36.75 343 | Objects removed this frame: 8025 344 | Objects added this frame: 15000 345 | -------------------------- 346 | Frame: 2 347 | Loops per frame: 15000 348 | Pool current size: 6952 349 | Total update calls: 104812066 350 | Iterations total time: 75.659 351 | Allocations total time: 0.07 352 | Remove objects total time: 53.76 353 | Objects removed this frame: 15024 354 | Objects added this frame: 15000 355 | -------------------------- 356 | Frame: 3 357 | Loops per frame: 15000 358 | Pool current size: 6935 359 | Total update calls: 104436172 360 | Iterations total time: 75.233 361 | Allocations total time: 0.118 362 | Remove objects total time: 53.833 363 | Objects removed this frame: 15017 364 | Objects added this frame: 15000 365 | -------------------------- 366 | Frame: 4 367 | Loops per frame: 15000 368 | Pool current size: 6949 369 | Total update calls: 104269843 370 | Iterations total time: 73.96 371 | Allocations total time: 0.157 372 | Remove objects total time: 52.76 373 | Objects removed this frame: 14986 374 | Objects added this frame: 15000 375 | -------------------------- 376 | 377 | 378 | ========================== 379 | TEST VECTOR 380 | ========================== 381 | 382 | Frame: 0 383 | Loops per frame: 15000 384 | Pool current size: 1 385 | Total update calls: 1 386 | Iterations total time: 0 387 | Allocations total time: 0 388 | Remove objects total time: 0 389 | Objects removed this frame: 0 390 | Objects added this frame: 1 391 | -------------------------- 392 | Frame: 1 393 | Loops per frame: 15000 394 | Pool current size: 6872 395 | Total update calls: 71811310 396 | Iterations total time: 25.1 397 | Allocations total time: 0.019 398 | Remove objects total time: 12.152 399 | Objects removed this frame: 15735 400 | Objects added this frame: 15000 401 | -------------------------- 402 | Frame: 2 403 | Loops per frame: 15000 404 | Pool current size: 6957 405 | Total update calls: 103485217 406 | Iterations total time: 36.328 407 | Allocations total time: 0.031 408 | Remove objects total time: 17.489 409 | Objects removed this frame: 29915 410 | Objects added this frame: 15000 411 | -------------------------- 412 | Frame: 3 413 | Loops per frame: 15000 414 | Pool current size: 6972 415 | Total update calls: 104276134 416 | Iterations total time: 36.259 417 | Allocations total time: 0.052 418 | Remove objects total time: 17.383 419 | Objects removed this frame: 29985 420 | Objects added this frame: 15000 421 | -------------------------- 422 | Frame: 4 423 | Loops per frame: 15000 424 | Pool current size: 6918 425 | Total update calls: 104338469 426 | Iterations total time: 36.525 427 | Allocations total time: 0.065 428 | Remove objects total time: 17.633 429 | Objects removed this frame: 30054 430 | Objects added this frame: 15000 431 | -------------------------- 432 | 433 | ``` 434 | 435 | #### Conclusion 436 | 437 | At the time of peak (eg when there were the most objects in pools), we measured the following times (all in seconds): 438 | 439 | ``` 440 | Dcm_pool List Vector 441 | Iterating 33.439 75.233 36.259 442 | Allocating 0.406 0.118 0.052 443 | Releasing 0.28 53.833 17.383 444 | Update Calls 104463885 104436172 104276134 445 | ``` 446 | 447 | 448 | Note: the Vector was iterated using an iterator. However, if we iterate the vector using a direct index its much faster and closer to the pool's performance. 449 | 450 | **If performance is pretty close to vector (despite in releasing) why not using a vector?** 451 | 452 | From the benchmark above you may come to the conclusion that a vector may be 'good enough', provided you don't have lots of releasing to do. However, keep in mind that you can't safely hold a pointer to an item inside a vector, since pushing / poping may change the underling addresses. The dcm_pool however, gives you vector-like performance but with faster releasing AND safe-to-use pointers to objects inside the pool. 453 | 454 | ## License 455 | 456 | dcm_pool is distributed under the MIT license and can be used for any purpose. 457 | --------------------------------------------------------------------------------