summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorXiaohong Bao <bao@lindenlab.com>2010-12-08 20:50:39 -0700
committerXiaohong Bao <bao@lindenlab.com>2010-12-08 20:50:39 -0700
commit43f4429363e63484f35663c10ca993d0d812e855 (patch)
tree5604de502d18fc1f1ade4157cd137f8a1e2e41b2 /indra/llcommon
parentf4ff1430f0d6ae7dd5a6be0bd665678b30a63aca (diff)
test code and some code change
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/llmemory.cpp251
-rw-r--r--indra/llcommon/llmemory.h62
2 files changed, 269 insertions, 44 deletions
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index ca06589611..a659e84309 100644
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -40,7 +40,7 @@
#include "llmemory.h"
#include "llsys.h"
-
+#include "llframetimer.h"
//----------------------------------------------------------------------------
@@ -505,6 +505,7 @@ void LLPrivateMemoryPool::LLMemoryChunk::init(char* buffer, U32 buffer_size, U32
{
mBuffer = buffer ;
mBufferSize = buffer_size ;
+ mAlloatedSize = 0 ;
mMetaBuffer = mBuffer + sizeof(LLMemoryChunk) ;
@@ -552,18 +553,16 @@ U32 LLPrivateMemoryPool::LLMemoryChunk::getMaxOverhead(U32 data_buffer_size, U32
char* LLPrivateMemoryPool::LLMemoryChunk::allocate(U32 size)
{
char* p = NULL ;
- U32 blk_idx = size / mMinSlotSize ;
- if(mMinSlotSize * blk_idx < size)
- {
- blk_idx++ ;
- }
+ U32 blk_idx = getBlockLevel(size);
+
+ LLMemoryBlock* blk = NULL ;
//check if there is free block available
if(mAvailBlockList[blk_idx])
{
- LLMemoryBlock* blk = mAvailBlockList[blk_idx] ;
+ blk = mAvailBlockList[blk_idx] ;
p = blk->allocate() ;
-
+
if(blk->isFull())
{
//removeFromFreelist
@@ -574,7 +573,7 @@ char* LLPrivateMemoryPool::LLMemoryChunk::allocate(U32 size)
//ask for a new block
if(!p)
{
- LLMemoryBlock* blk = addBlock(blk_idx) ;
+ blk = addBlock(blk_idx) ;
if(blk)
{
p = blk->allocate() ;
@@ -594,7 +593,7 @@ char* LLPrivateMemoryPool::LLMemoryChunk::allocate(U32 size)
{
if(mAvailBlockList[i])
{
- LLMemoryBlock* blk = mAvailBlockList[i] ;
+ blk = mAvailBlockList[i] ;
p = blk->allocate() ;
if(blk->isFull())
@@ -607,16 +606,23 @@ char* LLPrivateMemoryPool::LLMemoryChunk::allocate(U32 size)
}
}
+ if(p && blk)
+ {
+ mAlloatedSize += blk->getSlotSize() ;
+ }
return p ;
}
void LLPrivateMemoryPool::LLMemoryChunk::free(void* addr)
{
- LLMemoryBlock* blk = (LLMemoryBlock*)(mMetaBuffer + (((char*)addr - mDataBuffer) / mMinBlockSize) * sizeof(LLMemoryBlock)) ;
+ U32 blk_idx = ((U32)addr - (U32)mDataBuffer) / mMinBlockSize ;
+ if(blk_idx > 0) blk_idx-- ;
+ LLMemoryBlock* blk = (LLMemoryBlock*)(mMetaBuffer + blk_idx * sizeof(LLMemoryBlock)) ;
blk = blk->mSelf ;
bool was_full = blk->isFull() ;
blk->free(addr) ;
+ mAlloatedSize -= blk->getSlotSize() ;
if(blk->empty())
{
@@ -628,13 +634,18 @@ void LLPrivateMemoryPool::LLMemoryChunk::free(void* addr)
}
}
+bool LLPrivateMemoryPool::LLMemoryChunk::empty()
+{
+ return !mAlloatedSize ;
+}
+
LLPrivateMemoryPool::LLMemoryBlock* LLPrivateMemoryPool::LLMemoryChunk::addBlock(U32 blk_idx)
{
U32 slot_size = mMinSlotSize * (blk_idx + 1) ;
U32 preferred_block_size = llmax(mMinBlockSize, slot_size * 32) ;
preferred_block_size = llmin(preferred_block_size, mMaxBlockSize) ;
- U32 idx = preferred_block_size / mMinBlockSize ;
+ U32 idx = preferred_block_size / mMinBlockSize - 1;
preferred_block_size = idx * mMinBlockSize ; //round to integer times of mMinBlockSize.
LLMemoryBlock* blk = NULL ;
@@ -710,7 +721,10 @@ LLPrivateMemoryPool::LLMemoryBlock* LLPrivateMemoryPool::LLMemoryChunk::createNe
else
{
*cur_idxp = blk->mNext ; //move to the next slot
- (*cur_idxp)->mPrev = NULL ;
+ if(*cur_idxp)
+ {
+ (*cur_idxp)->mPrev = NULL ;
+ }
addToFreeSpace(next_blk) ;
}
@@ -718,7 +732,10 @@ LLPrivateMemoryPool::LLMemoryBlock* LLPrivateMemoryPool::LLMemoryChunk::createNe
else //move to the next block
{
*cur_idxp = blk->mNext ;
- (*cur_idxp)->mPrev = NULL ;
+ if(*cur_idxp)
+ {
+ (*cur_idxp)->mPrev = NULL ;
+ }
}
//insert to the available block list...
@@ -791,7 +808,9 @@ void LLPrivateMemoryPool::LLMemoryChunk::popAvailBlockList(U32 blk_idx)
void LLPrivateMemoryPool::LLMemoryChunk::addToFreeSpace(LLMemoryBlock* blk)
{
- U16 free_idx = blk->getBufferSize() / mMinBlockSize ;
+ U16 free_idx = blk->getBufferSize() / mMinBlockSize;
+ if(free_idx > 0) free_idx--;
+
(blk + free_idx)->mSelf = blk ; //mark the end pointing back to the head.
free_idx = llmin(free_idx, (U16)(mPartitionLevels - 1)) ;
@@ -809,7 +828,8 @@ void LLPrivateMemoryPool::LLMemoryChunk::addToFreeSpace(LLMemoryBlock* blk)
void LLPrivateMemoryPool::LLMemoryChunk::removeFromFreeSpace(LLMemoryBlock* blk)
{
- U16 free_idx = blk->getBufferSize() / mMinBlockSize ;
+ U16 free_idx = blk->getBufferSize() / mMinBlockSize;
+ if(free_idx > 0) free_idx-- ;
free_idx = llmin(free_idx, (U16)(mPartitionLevels - 1)) ;
if(mFreeSpaceList[free_idx] == blk)
@@ -830,7 +850,7 @@ void LLPrivateMemoryPool::LLMemoryChunk::removeFromFreeSpace(LLMemoryBlock* blk)
void LLPrivateMemoryPool::LLMemoryChunk::addToAvailBlockList(LLMemoryBlock* blk)
{
- U32 blk_idx = blk->getSlotSize() / mMinSlotSize ;
+ U32 blk_idx = getBlockLevel(blk->getSlotSize());
blk->mNext = mAvailBlockList[blk_idx] ;
if(blk->mNext)
@@ -842,6 +862,16 @@ void LLPrivateMemoryPool::LLMemoryChunk::addToAvailBlockList(LLMemoryBlock* blk)
return ;
}
+U32 LLPrivateMemoryPool::LLMemoryChunk::getBlockLevel(U32 size)
+{
+ return (size + mMinSlotSize - 1) / mMinSlotSize - 1 ;
+}
+
+U32 LLPrivateMemoryPool::LLMemoryChunk::getPageLevel(U32 size)
+{
+ return (size + mMinBlockSize - 1) / mMinBlockSize - 1 ;
+}
+
//-------------------------------------------------------------------
//class LLPrivateMemoryPool
//--------------------------------------------------------------------
@@ -875,6 +905,11 @@ char* LLPrivateMemoryPool::allocate(U32 size)
{
const static U32 MAX_BLOCK_SIZE = 4 * 1024 * 1024 ; //4MB
+ if(!size)
+ {
+ return NULL ;
+ }
+
//if the asked size larger than MAX_BLOCK_SIZE, fetch from heap directly, the pool does not manage it
if(size >= MAX_BLOCK_SIZE)
{
@@ -902,7 +937,10 @@ char* LLPrivateMemoryPool::allocate(U32 size)
if(!p)
{
chunk = addChunk(chunk_idx) ;
- p = chunk->allocate(size) ;
+ if(chunk)
+ {
+ p = chunk->allocate(size) ;
+ }
}
unlock() ;
@@ -912,6 +950,11 @@ char* LLPrivateMemoryPool::allocate(U32 size)
void LLPrivateMemoryPool::free(void* addr)
{
+ if(!addr)
+ {
+ return ;
+ }
+
lock() ;
LLMemoryChunk* chunk = mChunks[findChunk((char*)addr)] ;
@@ -1116,7 +1159,7 @@ LLPrivateMemoryPoolTester* LLPrivateMemoryPoolTester::getInstance()
{
if(!sInstance)
{
- sInstance = new LLPrivateMemoryPoolTester() ;
+ sInstance = ::new LLPrivateMemoryPoolTester() ;
}
return sInstance ;
}
@@ -1126,51 +1169,181 @@ void LLPrivateMemoryPoolTester::destroy()
{
if(sInstance)
{
- delete sInstance ;
+ ::delete sInstance ;
sInstance = NULL ;
}
if(sPool)
{
- delete sPool ;
+ ::delete sPool ;
sPool = NULL ;
}
}
-void LLPrivateMemoryPoolTester::run()
+void LLPrivateMemoryPoolTester::run(bool threaded)
{
const U32 max_pool_size = 16 << 20 ;
- const bool threaded = false ;
- if(!sPool)
+
+ if(sPool)
{
- sPool = new LLPrivateMemoryPool(max_pool_size, threaded) ;
+ ::delete sPool ;
}
+ sPool = ::new LLPrivateMemoryPool(max_pool_size, threaded) ;
//run the test
correctnessTest() ;
- reliabilityTest() ;
performanceTest() ;
fragmentationtest() ;
+
+ //release pool.
+ ::delete sPool ;
+ sPool = NULL ;
+}
+
+void LLPrivateMemoryPoolTester::test(U32 min_size, U32 max_size, U32 stride, U32 times,
+ bool random_deletion, bool output_statistics)
+{
+ U32 levels = (max_size - min_size) / stride + 1 ;
+ char*** p ;
+ U32 i, j ;
+
+ //allocate space for p ;
+ if(!(p = ::new char**[times]) || !(*p = ::new char*[times * levels]))
+ {
+ llerrs << "memory initialization for p failed" << llendl ;
+ }
+
+ //init
+ for(i = 0 ; i < times; i++)
+ {
+ p[i] = *p + i * levels ;
+ for(j = 0 ; j < levels; j++)
+ {
+ p[i][j] = NULL ;
+ }
+ }
+
+ //allocation
+ U32 size ;
+ for(i = 0 ; i < times ; i++)
+ {
+ for(j = 0 ; j < levels; j++)
+ {
+ size = min_size + j * stride ;
+ p[i][j] = sPool->allocate(size) ;
+ p[i][j][size - 1] = '\0' ; //access the last element to verify the success of the allocation.
+
+ //randomly release memory
+ if(random_deletion)
+ {
+ S32 k = rand() % levels ;
+ sPool->free(p[i][k]) ;
+ p[i][k] = NULL ;
+ }
+ }
+ }
+
+ //output pool allocation statistics
+ if(output_statistics)
+ {
+ }
+
+ //release all memory allocations
+ for(i = 0 ; i < times; i++)
+ {
+ for(j = 0 ; j < levels; j++)
+ {
+ sPool->free(p[i][j]) ;
+ p[i][j] = NULL ;
+ }
+ }
+
+ ::delete[] *p ;
+ ::delete[] p ;
}
void LLPrivateMemoryPoolTester::correctnessTest()
{
- //try many different sized allocation, fill the memory fully to see if allocation is right.
+ //try many different sized allocation, and all kinds of edge cases, access the allocated memory
+ //to see if allocation is right.
+
+ //edge case
+ char* p = sPool->allocate(0) ;
+ sPool->free(p) ;
+
+ //small sized
+ // [8 bytes, 2KB), each asks for 256 allocations and deallocations
+ test(8, 2040, 8, 256, true, true) ;
+
+ //medium sized
+ //[2KB, 512KB), each asks for 16 allocations and deallocations
+ test(2048, 512 * 1024 - 2048, 2048, 16, true, true) ;
+ //large sized
+ //[512KB, 4MB], each asks for 8 allocations and deallocations
+ test(512 * 1024, 4 * 1024 * 1024, 64 * 1024, 8, true, true) ;
}
-void LLPrivateMemoryPoolTester::reliabilityTest()
void LLPrivateMemoryPoolTester::performanceTest()
+{
+ U32 test_size[3] = {768, 3* 1024, 3* 1024 * 1024};
+
+ S32 i ;
+ LLFrameTimer timer ;
+
+ //do 1024 various-sized allocations / deallocations, compare the performance with the normal ones.
+
+ //small sized
+ {
+ timer.reset() ;
+ char* p[1024] = {NULL} ;
+ for(i = 0 ; i < 1024; i++)
+ {
+ p[i] = sPool->allocate(test_size[0]) ;
+ if(!p[i])
+ {
+ llerrs << "allocation failed" << llendl ;
+ }
+ }
+
+ for(i = 0 ; i < 1024; i++)
+ {
+ sPool->free(p[i]) ;
+ p[i] = NULL ;
+ }
+ llinfos << "time spent on 1024 small allocations: %f " << timer.getElapsedTimeF32() << llendl ;
+
+ timer.reset() ;
+
+ //using the standard allocator/de-allocator:
+ for(i = 0 ; i < 1024; i++)
+ {
+ p[i] = ::new char[test_size[0]] ;
+ if(!p[i])
+ {
+ llerrs << "allocation failed" << llendl ;
+ }
+ }
+
+ for(i = 0 ; i < 1024; i++)
+ {
+ ::delete[] p[i] ;
+ p[i] = NULL ;
+ }
+ llinfos << "time spent on 1024 small allocations: %f using standard allocator/de-allocator." << timer.getElapsedTimeF32() << llendl ;
+
+ timer.reset() ;
+ }
+ //medium sized
+
+ //large sized
+}
+
void LLPrivateMemoryPoolTester::fragmentationtest()
+{
+ //for internal fragmentation statistics:
+ //every time when asking for a new chunk during correctness test, and performance test,
+ //print out the chunk usage statistices.
+}
-void* LLPrivateMemoryPoolTester::operator new(size_t size)
-{
- return (void*)sPool->allocate(size) ;
-}
-
-void LLPrivateMemoryPoolTester::operator delete(void* addr)
-{
- sPool->free(addr) ;
-}
-
-//--------------------------------------------------------------------
+//--------------------------------------------------------------------
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index d9e93d0e96..128e7aefe6 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -63,6 +63,14 @@ private:
static BOOL sEnableMemoryFailurePrevention;
};
+//
+//class LLPrivateMemoryPool defines a private memory pool for an application to use, so the application does not
+//need to access the heap directly fro each memory allocation. Throught this, the allocation speed is faster,
+//and reduces virtaul address space gragmentation problem.
+//Note: this class is thread-safe by passing true to the constructor function. However, you do not need to do this unless
+//you are sure the memory allocation and de-allocation will happen in different threads. To make the pool thread safe
+//increases allocation and deallocation cost.
+//
class LL_COMMON_API LLPrivateMemoryPool
{
public:
@@ -122,6 +130,8 @@ public:
static U32 getMaxOverhead(U32 data_buffer_size, U32 min_page_size) ;
private:
+ U32 getBlockLevel(U32 size) ;
+ U32 getPageLevel(U32 size) ;
LLMemoryBlock* addBlock(U32 blk_idx) ;
void popAvailBlockList(U32 blk_idx) ;
void addToFreeSpace(LLMemoryBlock* blk) ;
@@ -142,6 +152,7 @@ public:
U32 mMinBlockSize ;
U32 mMaxBlockSize;
U32 mMinSlotSize ;
+ U32 mAlloatedSize ;
U16 mBlockLevels;
U16 mPartitionLevels;
@@ -192,7 +203,7 @@ private:
//
//the below singleton is used to test the private memory pool.
//
-class LLPrivateMemoryPoolTester
+class LL_COMMON_API LLPrivateMemoryPoolTester
{
private:
LLPrivateMemoryPoolTester() ;
@@ -202,22 +213,63 @@ public:
static LLPrivateMemoryPoolTester* getInstance() ;
static void destroy() ;
- void run() ;
+ void run(bool threaded) ;
private:
void correctnessTest() ;
- void reliabilityTest() ;
void performanceTest() ;
void fragmentationtest() ;
- void* operator new(size_t);
- void operator delete(void*);
+ void test(U32 min_size, U32 max_size, U32 stride, U32 times, bool random_deletion, bool output_statistics) ;
+
+public:
+ void* operator new(size_t size)
+ {
+ return (void*)sPool->allocate(size) ;
+ }
+ void operator delete(void* addr)
+ {
+ sPool->free(addr) ;
+ }
+ void* operator new[](size_t size)
+ {
+ return (void*)sPool->allocate(size) ;
+ }
+ void operator delete[](void* addr)
+ {
+ sPool->free(addr) ;
+ }
private:
static LLPrivateMemoryPoolTester* sInstance;
static LLPrivateMemoryPool* sPool ;
+ static LLPrivateMemoryPool* sThreadedPool ;
};
+#if 0
+//static
+void* LLPrivateMemoryPoolTester::operator new(size_t size)
+{
+ return (void*)sPool->allocate(size) ;
+}
+
+//static
+void LLPrivateMemoryPoolTester::operator delete(void* addr)
+{
+ sPool->free(addr) ;
+}
+//static
+void* LLPrivateMemoryPoolTester::operator new[](size_t size)
+{
+ return (void*)sPool->allocate(size) ;
+}
+
+//static
+void LLPrivateMemoryPoolTester::operator delete[](void* addr)
+{
+ sPool->free(addr) ;
+}
+#endif
// LLRefCount moved to llrefcount.h
// LLPointer moved to llpointer.h