TuttleOFX
1
|
00001 #include "MemoryPool.hpp" 00002 00003 #include <tuttle/common/utils/global.hpp> 00004 #include <tuttle/common/system/memoryInfo.hpp> 00005 #include <tuttle/host/Core.hpp> 00006 00007 #include <boost/throw_exception.hpp> 00008 00009 #include <algorithm> 00010 00011 namespace tuttle { 00012 namespace host { 00013 namespace memory { 00014 00015 IPool::~IPool() {} 00016 00017 class PoolData : public IPoolData 00018 { 00019 private: 00020 PoolData(); ///< No default Ctor 00021 PoolData( const PoolData& ); ///< No copy Ctor 00022 friend class MemoryPool; 00023 00024 public: 00025 PoolData( IPool& pool, const std::size_t size ) 00026 : _pool( pool ) 00027 , _id( _count++ ) 00028 , _reservedSize( size ) 00029 , _size( size ) 00030 , _pData( new char[size] ) 00031 , _refCount( 0 ) 00032 {} 00033 00034 ~PoolData() 00035 { 00036 delete [] _pData; 00037 } 00038 00039 public: 00040 bool operator==( const PoolData& other ) const 00041 { 00042 return _id == other._id; 00043 } 00044 00045 void addRef(); 00046 void release(); 00047 00048 char* data() { return _pData; } 00049 const char* data() const { return _pData; } 00050 const std::size_t size() const { return _size; } 00051 const std::size_t reservedSize() const { return _reservedSize; } 00052 00053 void setSize( const std::size_t newSize ) 00054 { 00055 assert( newSize <= _reservedSize ); 00056 _size = newSize; 00057 } 00058 00059 private: 00060 static std::size_t _count; ///< unique id generator 00061 IPool& _pool; ///< ref to the owner pool 00062 const std::size_t _id; ///< unique id to identify one memory data 00063 const std::size_t _reservedSize; ///< memory allocated 00064 std::size_t _size; ///< memory requested 00065 char* const _pData; ///< own the data 00066 int _refCount; ///< counter on clients currently using this data 00067 }; 00068 00069 void intrusive_ptr_add_ref( IPoolData* pData ) 00070 { 00071 pData->addRef(); 00072 } 00073 00074 void intrusive_ptr_release( IPoolData* pData ) 00075 { 00076 pData->release(); 00077 } 00078 00079 std::size_t PoolData::_count = 0; 00080 00081 void PoolData::addRef() 00082 { 00083 if( ++_refCount == 1 ) 00084 _pool.referenced( this ); 00085 } 00086 00087 void PoolData::release() 00088 { 00089 if( --_refCount == 0 ) 00090 _pool.released( this ); 00091 } 00092 00093 MemoryPool::MemoryPool( const std::size_t maxSize ) 00094 : _memoryAuthorized( maxSize ) 00095 {} 00096 00097 MemoryPool::~MemoryPool() 00098 { 00099 if( !_dataUsed.empty() ) 00100 { 00101 TUTTLE_LOG_DEBUG( "[Memory Pool] Error inside memory pool. Some data always mark used at the destruction (nb elements:" << _dataUsed.size() << ")" ); 00102 } 00103 } 00104 00105 void MemoryPool::referenced( PoolData* pData ) 00106 { 00107 boost::mutex::scoped_lock locker( _mutex ); 00108 DataList::iterator it = _dataUnused.find( pData ); 00109 00110 if( it != _dataUnused.end() ) 00111 { 00112 _dataUnused.erase( it ); 00113 } 00114 else // a really new data 00115 { 00116 _allDatas.push_back( pData ); 00117 _dataMap[pData->data()] = pData; 00118 } 00119 _dataUsed.insert( pData ); 00120 } 00121 00122 void MemoryPool::released( PoolData* pData ) 00123 { 00124 boost::mutex::scoped_lock locker( _mutex ); 00125 _dataUsed.erase( pData ); 00126 _dataUnused.insert( pData ); 00127 } 00128 00129 namespace { 00130 00131 /// Functor to get the smallest element in pool 00132 struct DataFitSize : public std::unary_function<PoolData*, void> 00133 { 00134 DataFitSize( std::size_t size ) 00135 : _sizeNeeded( size ) 00136 , _bestMatchDiff( ULONG_MAX ) 00137 , _pBestMatch( NULL ) 00138 {} 00139 00140 void operator()( PoolData* pData ) 00141 { 00142 const std::size_t dataSize = pData->reservedSize(); 00143 00144 // Check minimum amount of memory 00145 if( _sizeNeeded > dataSize ) 00146 return; 00147 // Do not reuse too big buffers 00148 if( dataSize > _maxBufferRatio * _sizeNeeded ) 00149 return; 00150 const std::size_t diff = dataSize - _sizeNeeded; 00151 if( diff >= _bestMatchDiff ) 00152 return; 00153 _bestMatchDiff = diff; 00154 _pBestMatch = pData; 00155 } 00156 00157 PoolData* bestMatch() 00158 { 00159 return _pBestMatch; 00160 } 00161 00162 private: 00163 static const double _maxBufferRatio; //< max ratio between used and unused part of the buffer 00164 const std::size_t _sizeNeeded; 00165 std::size_t _bestMatchDiff; 00166 PoolData* _pBestMatch; 00167 }; 00168 00169 const double DataFitSize::_maxBufferRatio = 2.0; 00170 00171 } 00172 00173 IPoolDataPtr MemoryPool::allocate( const std::size_t size ) 00174 { 00175 // Try to reuse a buffer available in the MemoryPool 00176 IPoolData* pData = getOneAvailableData( size ); 00177 if( pData != NULL ) 00178 { 00179 TUTTLE_LOG_TRACE("[Memory Pool] Reuse a buffer available in the MemoryPool"); 00180 pData->setSize( size ); 00181 return pData; 00182 } 00183 00184 // Try to remove unused element in MemoryCache, and reuse the buffer available in the MemoryPool 00185 memory::IMemoryCache& memoryCache = core().getMemoryCache(); 00186 CACHE_ELEMENT unusedCacheElement = memoryCache.getUnusedWithSize( size ); 00187 if( unusedCacheElement.get() != NULL ) 00188 { 00189 TUTTLE_LOG_TRACE("[Memory Pool] Pop element in the MemoryCache from " << unusedCacheElement->getFullName() << " of size " << size); 00190 memoryCache.remove( unusedCacheElement ); 00191 00192 pData = getOneAvailableData( size ); 00193 if( pData != NULL ) 00194 { 00195 TUTTLE_LOG_TRACE("[Memory Pool] Reuse a buffer available in the MemoryPool"); 00196 pData->setSize( size ); 00197 return pData; 00198 } 00199 } 00200 00201 // Try to allocate a new buffer in MemoryPool 00202 std::size_t availableSize = getAvailableMemorySize(); 00203 if( size > availableSize ) 00204 { 00205 // Try to release elements from the MemoryCache (make them available to the MemoryPool) 00206 TUTTLE_LOG_TRACE("[Memory Pool] Release elements from the MemoryCache"); 00207 memoryCache.clearUnused(); 00208 00209 availableSize = getAvailableMemorySize(); 00210 if( size > availableSize ) 00211 { 00212 // Release elements from the MemoryPool (make them available to the OS) 00213 TUTTLE_LOG_TRACE("[Memory Pool] Release elements from the MemoryPool"); 00214 clear(); 00215 } 00216 00217 availableSize = getAvailableMemorySize(); 00218 if( size > availableSize ) 00219 { 00220 std::stringstream s; 00221 s << "[Memory Pool] can't allocate size:" << size << " because memory available is equal to " << availableSize << " bytes"; 00222 BOOST_THROW_EXCEPTION( std::length_error( s.str() ) ); 00223 } 00224 } 00225 00226 // Allocate a new buffer in MemoryPool 00227 TUTTLE_TLOG( TUTTLE_TRACE, "[Memory Pool] allocate " << size << " bytes" ); 00228 return new PoolData( *this, size ); 00229 } 00230 00231 std::size_t MemoryPool::updateMemoryAuthorizedWithRAM() 00232 { 00233 _memoryAuthorized = /*getUsedMemorySize() +*/ getMemoryInfo()._totalRam; 00234 TUTTLE_LOG_DEBUG( TUTTLE_TRACE, "[Memory Pool] update memory authorized with RAM: " << _memoryAuthorized ); 00235 return _memoryAuthorized; 00236 } 00237 00238 namespace { 00239 00240 std::size_t accumulateReservedSize( const std::size_t& sum, const IPoolData* pData ) 00241 { 00242 return sum + pData->reservedSize(); 00243 } 00244 00245 std::size_t accumulateWastedSize( const std::size_t& sum, const IPoolData* pData ) 00246 { 00247 return sum + ( pData->reservedSize() - pData->size() ); 00248 } 00249 00250 } 00251 00252 std::size_t MemoryPool::getUsedMemorySize() const 00253 { 00254 boost::mutex::scoped_lock locker( _mutex ); 00255 return std::accumulate( _dataUsed.begin(), _dataUsed.end(), 0, &accumulateReservedSize ); 00256 } 00257 00258 std::size_t MemoryPool::getAllocatedAndUnusedMemorySize() const 00259 { 00260 boost::mutex::scoped_lock locker( _mutex ); 00261 return std::accumulate( _dataUnused.begin(), _dataUnused.end(), 0, &accumulateReservedSize ); 00262 } 00263 00264 std::size_t MemoryPool::getAllocatedMemorySize() const 00265 { 00266 return getUsedMemorySize() + getAllocatedAndUnusedMemorySize(); 00267 } 00268 00269 std::size_t MemoryPool::getMaxMemorySize() const 00270 { 00271 return _memoryAuthorized; 00272 } 00273 00274 std::size_t MemoryPool::getAvailableMemorySize() const 00275 { 00276 return getMaxMemorySize() - getUsedMemorySize(); 00277 } 00278 00279 std::size_t MemoryPool::getWastedMemorySize() const 00280 { 00281 boost::mutex::scoped_lock locker( _mutex ); 00282 return std::accumulate( _dataUsed.begin(), _dataUsed.end(), 0, std::ptr_fun( &accumulateWastedSize ) ); 00283 } 00284 00285 std::size_t MemoryPool::getDataUsedSize() const 00286 { 00287 return _dataUsed.size(); 00288 } 00289 00290 std::size_t MemoryPool::getDataUnusedSize() const 00291 { 00292 return _dataUnused.size(); 00293 } 00294 00295 PoolData* MemoryPool::getOneAvailableData( const size_t size ) 00296 { 00297 boost::mutex::scoped_lock locker( _mutex ); 00298 return std::for_each( _dataUnused.begin(), _dataUnused.end(), DataFitSize( size ) ).bestMatch(); 00299 } 00300 00301 void MemoryPool::clear( std::size_t size ) 00302 { 00303 /// @todo tuttle 00304 } 00305 00306 void MemoryPool::clear() 00307 { 00308 boost::mutex::scoped_lock locker( _mutex ); 00309 _dataUnused.clear(); 00310 } 00311 00312 void MemoryPool::clearOne() 00313 { 00314 boost::mutex::scoped_lock locker( _mutex ); 00315 _dataUnused.erase( _dataUnused.begin() ); 00316 } 00317 00318 std::ostream& operator<<( std::ostream& os, const MemoryPool& memoryPool ) 00319 { 00320 os << "[Memory Pool] Unused data: " << memoryPool.getDataUnusedSize() << " bytes\n"; 00321 os << "[Memory Pool] All datas: " << memoryPool.getDataUsedSize() << " bytes\n"; 00322 os << "[Memory Pool] Total RAM: " << getMemoryInfo()._totalRam << " bytes\n"; 00323 os << "\n"; 00324 os << "[Memory Pool] Used memory: " << memoryPool.getUsedMemorySize() << " bytes\n"; 00325 os << "[Memory Pool] Allocated memory: " << memoryPool.getAllocatedMemorySize() << " bytes\n"; 00326 os << "[Memory Pool] Max memory: " << memoryPool.getMaxMemorySize() << " bytes\n"; 00327 os << "[Memory Pool] Available memory size: " << memoryPool.getAvailableMemorySize() << " bytes\n"; 00328 os << "[Memory Pool] Wasted memory: " << memoryPool.getWastedMemorySize() << " bytes\n"; 00329 return os; 00330 } 00331 00332 } 00333 } 00334 }