/*M/////////////////////////////////////////////////////////////////////////////////////// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/ #include "precomp.hpp" #define CV_USE_SYSTEM_MALLOC 1 namespace cv { static void* OutOfMemoryError(size_t size) { CV_Error_(CV_StsNoMem, ("Failed to allocate %lu bytes", (unsigned long)size)); return 0; } #if CV_USE_SYSTEM_MALLOC #if defined _WIN32 void deleteThreadAllocData() {} #endif void* fastMalloc( size_t size ) { uchar* udata = (uchar*)malloc(size + sizeof(void*) + CV_MALLOC_ALIGN); if(!udata) return OutOfMemoryError(size); uchar** adata = alignPtr((uchar**)udata + 1, CV_MALLOC_ALIGN); adata[-1] = udata; return adata; } void fastFree(void* ptr) { if(ptr) { uchar* udata = ((uchar**)ptr)[-1]; CV_DbgAssert(udata < (uchar*)ptr && ((uchar*)ptr - udata) <= (ptrdiff_t)(sizeof(void*)+CV_MALLOC_ALIGN)); free(udata); } } #else //CV_USE_SYSTEM_MALLOC #if 0 #define SANITY_CHECK(block) \ CV_Assert(((size_t)(block) & (MEM_BLOCK_SIZE-1)) == 0 && \ (unsigned)(block)->binIdx <= (unsigned)MAX_BIN && \ (block)->signature == MEM_BLOCK_SIGNATURE) #else #define SANITY_CHECK(block) #endif #define STAT(stmt) #ifdef _WIN32 #if (_WIN32_WINNT >= 0x0602) #include #endif struct CriticalSection { CriticalSection() { #if (_WIN32_WINNT >= 0x0600) InitializeCriticalSectionEx(&cs, 1000, 0); #else InitializeCriticalSection(&cs); #endif } ~CriticalSection() { DeleteCriticalSection(&cs); } void lock() { EnterCriticalSection(&cs); } void unlock() { LeaveCriticalSection(&cs); } bool trylock() { return TryEnterCriticalSection(&cs) != 0; } CRITICAL_SECTION cs; }; void* SystemAlloc(size_t size) { void* ptr = malloc(size); return ptr ? ptr : OutOfMemoryError(size); } void SystemFree(void* ptr, size_t) { free(ptr); } #else //_WIN32 #include struct CriticalSection { CriticalSection() { pthread_mutex_init(&mutex, 0); } ~CriticalSection() { pthread_mutex_destroy(&mutex); } void lock() { pthread_mutex_lock(&mutex); } void unlock() { pthread_mutex_unlock(&mutex); } bool trylock() { return pthread_mutex_trylock(&mutex) == 0; } pthread_mutex_t mutex; }; void* SystemAlloc(size_t size) { #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif void* ptr = 0; ptr = mmap(ptr, size, (PROT_READ | PROT_WRITE), MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); return ptr != MAP_FAILED ? ptr : OutOfMemoryError(size); } void SystemFree(void* ptr, size_t size) { munmap(ptr, size); } #endif //_WIN32 struct AutoLock { AutoLock(CriticalSection& _cs) : cs(&_cs) { cs->lock(); } ~AutoLock() { cs->unlock(); } CriticalSection* cs; }; const size_t MEM_BLOCK_SIGNATURE = 0x01234567; const int MEM_BLOCK_SHIFT = 14; const size_t MEM_BLOCK_SIZE = 1 << MEM_BLOCK_SHIFT; const size_t HDR_SIZE = 128; const size_t MAX_BLOCK_SIZE = MEM_BLOCK_SIZE - HDR_SIZE; const int MAX_BIN = 28; static const int binSizeTab[MAX_BIN+1] = { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 128, 160, 192, 256, 320, 384, 480, 544, 672, 768, 896, 1056, 1328, 1600, 2688, 4048, 5408, 8128, 16256 }; struct MallocTables { void initBinTab() { int i, j = 0, n; for( i = 0; i <= MAX_BIN; i++ ) { n = binSizeTab[i]>>3; for( ; j <= n; j++ ) binIdx[j] = (uchar)i; } } int bin(size_t size) { assert( size <= MAX_BLOCK_SIZE ); return binIdx[(size + 7)>>3]; } MallocTables() { initBinTab(); } uchar binIdx[MAX_BLOCK_SIZE/8+1]; }; MallocTables mallocTables; struct Node { Node* next; }; struct ThreadData; struct Block { Block(Block* _next) { signature = MEM_BLOCK_SIGNATURE; prev = 0; next = _next; privateFreeList = publicFreeList = 0; bumpPtr = endPtr = 0; objSize = 0; threadData = 0; data = (uchar*)this + HDR_SIZE; } ~Block() {} void init(Block* _prev, Block* _next, int _objSize, ThreadData* _threadData) { prev = _prev; if(prev) prev->next = this; next = _next; if(next) next->prev = this; objSize = _objSize; binIdx = mallocTables.bin(objSize); threadData = _threadData; privateFreeList = publicFreeList = 0; bumpPtr = data; int nobjects = MAX_BLOCK_SIZE/objSize; endPtr = bumpPtr + nobjects*objSize; almostEmptyThreshold = (nobjects + 1)/2; allocated = 0; } bool isFilled() const { return allocated > almostEmptyThreshold; } size_t signature; Block* prev; Block* next; Node* privateFreeList; Node* publicFreeList; uchar* bumpPtr; uchar* endPtr; uchar* data; ThreadData* threadData; int objSize; int binIdx; int allocated; int almostEmptyThreshold; CriticalSection cs; }; struct BigBlock { BigBlock(int bigBlockSize, BigBlock* _next) { first = alignPtr((Block*)(this+1), MEM_BLOCK_SIZE); next = _next; nblocks = (int)(((char*)this + bigBlockSize - (char*)first)/MEM_BLOCK_SIZE); Block* p = 0; for( int i = nblocks-1; i >= 0; i-- ) p = ::new((uchar*)first + i*MEM_BLOCK_SIZE) Block(p); } ~BigBlock() { for( int i = nblocks-1; i >= 0; i-- ) ((Block*)((uchar*)first+i*MEM_BLOCK_SIZE))->~Block(); } BigBlock* next; Block* first; int nblocks; }; struct BlockPool { BlockPool(int _bigBlockSize=1<<20) : pool(0), bigBlockSize(_bigBlockSize) { } ~BlockPool() { AutoLock lock(cs); while( pool ) { BigBlock* nextBlock = pool->next; pool->~BigBlock(); SystemFree(pool, bigBlockSize); pool = nextBlock; } } Block* alloc() { AutoLock lock(cs); Block* block; if( !freeBlocks ) { BigBlock* bblock = ::new(SystemAlloc(bigBlockSize)) BigBlock(bigBlockSize, pool); assert( bblock != 0 ); freeBlocks = bblock->first; pool = bblock; } block = freeBlocks; freeBlocks = freeBlocks->next; if( freeBlocks ) freeBlocks->prev = 0; STAT(stat.bruttoBytes += MEM_BLOCK_SIZE); return block; } void free(Block* block) { AutoLock lock(cs); block->prev = 0; block->next = freeBlocks; freeBlocks = block; STAT(stat.bruttoBytes -= MEM_BLOCK_SIZE); } CriticalSection cs; Block* freeBlocks; BigBlock* pool; int bigBlockSize; int blocksPerBigBlock; }; BlockPool mallocPool; enum { START=0, FREE=1, GC=2 }; struct ThreadData { ThreadData() { for(int i = 0; i <= MAX_BIN; i++) bins[i][START] = bins[i][FREE] = bins[i][GC] = 0; } ~ThreadData() { // mark all the thread blocks as abandoned or even release them for( int i = 0; i <= MAX_BIN; i++ ) { Block *bin = bins[i][START], *block = bin; bins[i][START] = bins[i][FREE] = bins[i][GC] = 0; if( block ) { do { Block* next = block->next; int allocated = block->allocated; { AutoLock lock(block->cs); block->next = block->prev = 0; block->threadData = 0; Node *node = block->publicFreeList; for( ; node != 0; node = node->next ) allocated--; } if( allocated == 0 ) mallocPool.free(block); block = next; } while( block != bin ); } } } void moveBlockToFreeList( Block* block ) { int i = block->binIdx; Block*& freePtr = bins[i][FREE]; CV_DbgAssert( block->next->prev == block && block->prev->next == block ); if( block != freePtr ) { Block*& gcPtr = bins[i][GC]; if( gcPtr == block ) gcPtr = block->next; if( block->next != block ) { block->prev->next = block->next; block->next->prev = block->prev; } block->next = freePtr->next; block->prev = freePtr; freePtr = block->next->prev = block->prev->next = block; } } Block* bins[MAX_BIN+1][3]; #ifdef _WIN32 #ifdef WINCE # define TLS_OUT_OF_INDEXES ((DWORD)0xFFFFFFFF) #endif //WINCE static DWORD tlsKey; static ThreadData* get() { ThreadData* data; if( tlsKey == TLS_OUT_OF_INDEXES ) tlsKey = TlsAlloc(); data = (ThreadData*)TlsGetValue(tlsKey); if( !data ) { data = new ThreadData; TlsSetValue(tlsKey, data); } return data; } #else //_WIN32 static void deleteData(void* data) { delete (ThreadData*)data; } static pthread_key_t tlsKey; static ThreadData* get() { ThreadData* data; if( !tlsKey ) pthread_key_create(&tlsKey, deleteData); data = (ThreadData*)pthread_getspecific(tlsKey); if( !data ) { data = new ThreadData; pthread_setspecific(tlsKey, data); } return data; } #endif //_WIN32 }; #ifdef _WIN32 DWORD ThreadData::tlsKey = TLS_OUT_OF_INDEXES; void deleteThreadAllocData() { if( ThreadData::tlsKey != TLS_OUT_OF_INDEXES ) delete (ThreadData*)TlsGetValue( ThreadData::tlsKey ); } #else //_WIN32 pthread_key_t ThreadData::tlsKey = 0; #endif //_WIN32 #if 0 static void checkList(ThreadData* tls, int idx) { Block* block = tls->bins[idx][START]; if( !block ) { CV_DbgAssert( tls->bins[idx][FREE] == 0 && tls->bins[idx][GC] == 0 ); } else { bool gcInside = false; bool freeInside = false; do { if( tls->bins[idx][FREE] == block ) freeInside = true; if( tls->bins[idx][GC] == block ) gcInside = true; block = block->next; } while( block != tls->bins[idx][START] ); CV_DbgAssert( gcInside && freeInside ); } } #else #define checkList(tls, idx) #endif void* fastMalloc( size_t size ) { if( size > MAX_BLOCK_SIZE ) { size_t size1 = size + sizeof(uchar*)*2 + MEM_BLOCK_SIZE; uchar* udata = (uchar*)SystemAlloc(size1); uchar** adata = alignPtr((uchar**)udata + 2, MEM_BLOCK_SIZE); adata[-1] = udata; adata[-2] = (uchar*)size1; return adata; } { ThreadData* tls = ThreadData::get(); int idx = mallocTables.bin(size); Block*& startPtr = tls->bins[idx][START]; Block*& gcPtr = tls->bins[idx][GC]; Block*& freePtr = tls->bins[idx][FREE], *block = freePtr; checkList(tls, idx); size = binSizeTab[idx]; STAT( stat.nettoBytes += size; stat.mallocCalls++; ); uchar* data = 0; for(;;) { if( block ) { // try to find non-full block for(;;) { CV_DbgAssert( block->next->prev == block && block->prev->next == block ); if( block->bumpPtr ) { data = block->bumpPtr; if( (block->bumpPtr += size) >= block->endPtr ) block->bumpPtr = 0; break; } if( block->privateFreeList ) { data = (uchar*)block->privateFreeList; block->privateFreeList = block->privateFreeList->next; break; } if( block == startPtr ) break; block = block->next; } #if 0 avg_k += _k; avg_nk++; if( avg_nk == 1000 ) { printf("avg search iters per 1e3 allocs = %g\n", (double)avg_k/avg_nk ); avg_k = avg_nk = 0; } #endif freePtr = block; if( !data ) { block = gcPtr; for( int k = 0; k < 2; k++ ) { SANITY_CHECK(block); CV_DbgAssert( block->next->prev == block && block->prev->next == block ); if( block->publicFreeList ) { { AutoLock lock(block->cs); block->privateFreeList = block->publicFreeList; block->publicFreeList = 0; } Node* node = block->privateFreeList; for(;node != 0; node = node->next) --block->allocated; data = (uchar*)block->privateFreeList; block->privateFreeList = block->privateFreeList->next; gcPtr = block->next; if( block->allocated+1 <= block->almostEmptyThreshold ) tls->moveBlockToFreeList(block); break; } block = block->next; } if( !data ) gcPtr = block; } } if( data ) break; block = mallocPool.alloc(); block->init(startPtr ? startPtr->prev : block, startPtr ? startPtr : block, (int)size, tls); if( !startPtr ) startPtr = gcPtr = freePtr = block; checkList(tls, block->binIdx); SANITY_CHECK(block); } ++block->allocated; return data; } } void fastFree( void* ptr ) { if( ((size_t)ptr & (MEM_BLOCK_SIZE-1)) == 0 ) { if( ptr != 0 ) { void* origPtr = ((void**)ptr)[-1]; size_t sz = (size_t)((void**)ptr)[-2]; SystemFree( origPtr, sz ); } return; } { ThreadData* tls = ThreadData::get(); Node* node = (Node*)ptr; Block* block = (Block*)((size_t)ptr & -(int)MEM_BLOCK_SIZE); assert( block->signature == MEM_BLOCK_SIGNATURE ); if( block->threadData == tls ) { STAT( stat.nettoBytes -= block->objSize; stat.freeCalls++; float ratio = (float)stat.nettoBytes/stat.bruttoBytes; if( stat.minUsageRatio > ratio ) stat.minUsageRatio = ratio; ); SANITY_CHECK(block); bool prevFilled = block->isFilled(); --block->allocated; if( !block->isFilled() && (block->allocated == 0 || prevFilled) ) { if( block->allocated == 0 ) { int idx = block->binIdx; Block*& startPtr = tls->bins[idx][START]; Block*& freePtr = tls->bins[idx][FREE]; Block*& gcPtr = tls->bins[idx][GC]; if( block == block->next ) { CV_DbgAssert( startPtr == block && freePtr == block && gcPtr == block ); startPtr = freePtr = gcPtr = 0; } else { if( freePtr == block ) freePtr = block->next; if( gcPtr == block ) gcPtr = block->next; if( startPtr == block ) startPtr = block->next; block->prev->next = block->next; block->next->prev = block->prev; } mallocPool.free(block); checkList(tls, idx); return; } tls->moveBlockToFreeList(block); } node->next = block->privateFreeList; block->privateFreeList = node; } else { AutoLock lock(block->cs); SANITY_CHECK(block); node->next = block->publicFreeList; block->publicFreeList = node; if( block->threadData == 0 ) { // take ownership of the abandoned block. // note that it can happen at the same time as // ThreadData::deleteData() marks the blocks as abandoned, // so this part of the algorithm needs to be checked for data races int idx = block->binIdx; block->threadData = tls; Block*& startPtr = tls->bins[idx][START]; if( startPtr ) { block->next = startPtr; block->prev = startPtr->prev; block->next->prev = block->prev->next = block; } else startPtr = tls->bins[idx][FREE] = tls->bins[idx][GC] = block; } } } } #endif //CV_USE_SYSTEM_MALLOC } CV_IMPL void* cvAlloc( size_t size ) { return cv::fastMalloc( size ); } CV_IMPL void cvFree_( void* ptr ) { cv::fastFree( ptr ); } /* End of file. */