mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 03:23:03 +01:00
Further class library development. MemoryPool works now
This commit is contained in:
parent
6a290efb47
commit
77761a16b9
@ -35,10 +35,12 @@
|
|||||||
|
|
||||||
#define FB_MAX(M,N) ((M)>(N)?(M):(N))
|
#define FB_MAX(M,N) ((M)>(N)?(M):(N))
|
||||||
|
|
||||||
// TODO:
|
// TODO (in order of importance):
|
||||||
// 1. red zones checking
|
// 1. Pool size limit
|
||||||
// 2. alloc/free pattern
|
// 2. debug alloc/free pattern
|
||||||
// 3. line number debug info
|
// 3. line number debug info
|
||||||
|
// 4. pool locking and allocation source
|
||||||
|
// 5. red zones checking (not really needed because verify_pool is able to detect most corruption cases)
|
||||||
|
|
||||||
namespace Firebird {
|
namespace Firebird {
|
||||||
|
|
||||||
@ -50,8 +52,42 @@ static void pool_out_of_memory()
|
|||||||
throw std::bad_alloc();
|
throw std::bad_alloc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemoryPool::updateSpare() {
|
||||||
|
updatingSpare = true; // This is to prevent re-enterance
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
do {
|
||||||
|
needSpare = false;
|
||||||
|
while (spareLeafs.getCount() < spareLeafs.getCapacity())
|
||||||
|
spareLeafs.add(alloc(sizeof(FreeBlocksTree::ItemList), TYPE_LEAFPAGE));
|
||||||
|
while (spareNodes.getCount() <= freeBlocks.level)
|
||||||
|
spareNodes.add(alloc(sizeof(FreeBlocksTree::NodeList), TYPE_TREEPAGE));
|
||||||
|
break;
|
||||||
|
} while (needSpare);
|
||||||
|
// Great, if we were able to restore free blocks tree operations after critically low
|
||||||
|
// memory condition then try to add pending free blocks to our tree
|
||||||
|
while (pendingFree) {
|
||||||
|
PendingFreeBlock *temp = pendingFree;
|
||||||
|
pendingFree = temp->next;
|
||||||
|
MemoryBlock *blk = (MemoryBlock*)((char*)temp-ALIGN(sizeof(MemoryBlock)));
|
||||||
|
BlockInfo info = {
|
||||||
|
blk,
|
||||||
|
blk->length
|
||||||
|
};
|
||||||
|
internalAlloc = true;
|
||||||
|
freeBlocks.add(info); // We should be able to do this because we had all needed spare blocks
|
||||||
|
internalAlloc = false;
|
||||||
|
}
|
||||||
|
} catch(...) {
|
||||||
|
// We can always recover after this
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (needSpare);
|
||||||
|
updatingSpare = false;
|
||||||
|
}
|
||||||
|
|
||||||
void* MemoryPool::external_alloc(size_t size) {
|
void* MemoryPool::external_alloc(size_t size) {
|
||||||
// This method is assumed to throw exceptions in case it cannot alloc
|
// This method is assumed to return NULL in case it cannot alloc
|
||||||
return malloc(size);
|
return malloc(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,9 +192,7 @@ MemoryPool* MemoryPool::createPool() {
|
|||||||
blk->prev = hdr;
|
blk->prev = hdr;
|
||||||
BlockInfo temp = {blk, blockLength};
|
BlockInfo temp = {blk, blockLength};
|
||||||
pool->freeBlocks.add(temp);
|
pool->freeBlocks.add(temp);
|
||||||
// This code may not work if tree factor is 2 (but if MIN_EXTENT_SIZE is large enough it will)
|
pool->updateSpare();
|
||||||
pool->spareLeaf = pool->alloc(sizeof(FreeBlocksTree::ItemList));
|
|
||||||
pool->spareNodes.add(pool->alloc(sizeof(FreeBlocksTree::NodeList)));
|
|
||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,11 +210,13 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
|||||||
if (internalAlloc) {
|
if (internalAlloc) {
|
||||||
if (size == sizeof(FreeBlocksTree::ItemList))
|
if (size == sizeof(FreeBlocksTree::ItemList))
|
||||||
// This condition is to handle case when nodelist and itemlist have equal size
|
// This condition is to handle case when nodelist and itemlist have equal size
|
||||||
if (sizeof(FreeBlocksTree::ItemList)!=sizeof(FreeBlocksTree::ItemList) || spareLeaf) {
|
if (sizeof(FreeBlocksTree::ItemList)!=sizeof(FreeBlocksTree::NodeList) ||
|
||||||
void *temp = spareLeaf;
|
spareLeafs.getCount())
|
||||||
spareLeaf = NULL;
|
{
|
||||||
|
if (!spareLeafs.getCount()) pool_out_of_memory();
|
||||||
|
void *temp = spareLeafs[spareLeafs.getCount()-1];
|
||||||
|
spareLeafs.shrink(spareLeafs.getCount()-1);
|
||||||
needSpare = true;
|
needSpare = true;
|
||||||
if (!temp) pool_out_of_memory();
|
|
||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
if (size == sizeof(FreeBlocksTree::NodeList)) {
|
if (size == sizeof(FreeBlocksTree::NodeList)) {
|
||||||
@ -204,7 +240,9 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
|||||||
current->block->used = true;
|
current->block->used = true;
|
||||||
current->block->type = type;
|
current->block->type = type;
|
||||||
result = (char *)current->block + ALIGN(sizeof(MemoryBlock));
|
result = (char *)current->block + ALIGN(sizeof(MemoryBlock));
|
||||||
|
internalAlloc = true;
|
||||||
freeBlocks.fastRemove();
|
freeBlocks.fastRemove();
|
||||||
|
internalAlloc = false;
|
||||||
} else {
|
} else {
|
||||||
// Cut a piece at the end of block in hope to avoid structural
|
// Cut a piece at the end of block in hope to avoid structural
|
||||||
// modification of free blocks tree
|
// modification of free blocks tree
|
||||||
@ -221,7 +259,7 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
|||||||
if (!blk->last)
|
if (!blk->last)
|
||||||
((MemoryBlock *)((char*)blk + ALIGN(sizeof(MemoryBlock)) + blk->length))->prev = blk;
|
((MemoryBlock *)((char*)blk + ALIGN(sizeof(MemoryBlock)) + blk->length))->prev = blk;
|
||||||
// Update tree of free blocks
|
// Update tree of free blocks
|
||||||
if (!freeBlocks.getPrev() || freeBlocks.current().length <= current->block->length)
|
if (!freeBlocks.getPrev() || freeBlocks.current().length < current->block->length)
|
||||||
current->length = current->block->length;
|
current->length = current->block->length;
|
||||||
else {
|
else {
|
||||||
// Tree needs to be modified structurally
|
// Tree needs to be modified structurally
|
||||||
@ -230,23 +268,15 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
|||||||
#else
|
#else
|
||||||
bool res = freeBlocks.getNext();
|
bool res = freeBlocks.getNext();
|
||||||
assert(res);
|
assert(res);
|
||||||
|
assert(&freeBlocks.current()==current);
|
||||||
#endif
|
#endif
|
||||||
BlockInfo temp = {current->block, current->block->length};
|
|
||||||
freeBlocks.fastRemove();
|
|
||||||
internalAlloc = true;
|
internalAlloc = true;
|
||||||
try {
|
MemoryBlock *block = current->block;
|
||||||
freeBlocks.add(temp);
|
freeBlocks.fastRemove();
|
||||||
} catch(...) {
|
addFreeBlock(block);
|
||||||
// Add item to the list of pending free blocks in case of critically-low memory condition
|
|
||||||
PendingFreeBlock* temp =
|
|
||||||
(PendingFreeBlock *)((char *)freeBlocks.getAddErrorValue().block +
|
|
||||||
ALIGN(sizeof(MemoryBlock)));
|
|
||||||
temp->next = pendingFree;
|
|
||||||
pendingFree = temp;
|
|
||||||
}
|
|
||||||
internalAlloc = false;
|
internalAlloc = false;
|
||||||
}
|
}
|
||||||
result = blk+1;
|
result = (char*)blk+ALIGN(sizeof(MemoryBlock));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If we are in a critically low memory condition look up for a block in a list
|
// If we are in a critically low memory condition look up for a block in a list
|
||||||
@ -263,7 +293,7 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
|||||||
if (prev)
|
if (prev)
|
||||||
prev->next = itr->next;
|
prev->next = itr->next;
|
||||||
else
|
else
|
||||||
pendingFree = itr->next;
|
pendingFree = itr->next;
|
||||||
return itr;
|
return itr;
|
||||||
} else {
|
} else {
|
||||||
// Cut a piece at the end of block
|
// Cut a piece at the end of block
|
||||||
@ -315,158 +345,107 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
|||||||
rest->length = alloc_size - ALIGN(sizeof(MemoryExtent)) -
|
rest->length = alloc_size - ALIGN(sizeof(MemoryExtent)) -
|
||||||
ALIGN(sizeof(MemoryBlock)) - size - ALIGN(sizeof(MemoryBlock));
|
ALIGN(sizeof(MemoryBlock)) - size - ALIGN(sizeof(MemoryBlock));
|
||||||
rest->prev = blk;
|
rest->prev = blk;
|
||||||
BlockInfo temp = {rest, rest->length};
|
|
||||||
internalAlloc = true;
|
internalAlloc = true;
|
||||||
try {
|
addFreeBlock(rest);
|
||||||
freeBlocks.add(temp);
|
|
||||||
} catch(...) {
|
|
||||||
// Add item to the list of pending free blocks in case of critically-low memory condition
|
|
||||||
PendingFreeBlock* temp = (PendingFreeBlock *)((char*)freeBlocks.getAddErrorValue().block+
|
|
||||||
ALIGN(sizeof(MemoryBlock)));
|
|
||||||
temp->next = pendingFree;
|
|
||||||
pendingFree = temp;
|
|
||||||
}
|
|
||||||
internalAlloc = false;
|
internalAlloc = false;
|
||||||
}
|
}
|
||||||
result = (char*)blk+ALIGN(sizeof(MemoryBlock));
|
result = (char*)blk+ALIGN(sizeof(MemoryBlock));
|
||||||
}
|
}
|
||||||
// Grow spare blocks pool if necessary
|
// Grow spare blocks pool if necessary
|
||||||
if (needSpare) {
|
if (needSpare && !updatingSpare) updateSpare();
|
||||||
try {
|
|
||||||
if (!spareLeaf)
|
|
||||||
spareLeaf = alloc(sizeof(FreeBlocksTree::ItemList), TYPE_LEAFPAGE);
|
|
||||||
while (spareNodes.getCount() <= freeBlocks.level)
|
|
||||||
spareNodes.add(alloc(sizeof(FreeBlocksTree::NodeList), TYPE_TREEPAGE));
|
|
||||||
needSpare = false;
|
|
||||||
// We do not try to add pending blocks here because it is REALLY unlikely
|
|
||||||
// that we'll be able to recover after critically low memory condition
|
|
||||||
// during alloc()
|
|
||||||
} catch(...) {
|
|
||||||
// We can recover after this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryPool::free(void *block) {
|
void MemoryPool::addFreeBlock(MemoryBlock *blk) {
|
||||||
MemoryBlock *blk = (MemoryBlock *)((char*)block - ALIGN(sizeof(MemoryBlock))), *prev;
|
BlockInfo info = {blk, blk->length};
|
||||||
// Try to merge block with preceding free block
|
try {
|
||||||
if ((prev = blk->prev) && !prev->used) {
|
freeBlocks.add(info);
|
||||||
BlockInfo temp = {prev, prev->length};
|
} catch(...) {
|
||||||
if (freeBlocks.locate(temp)) {
|
// Add item to the list of pending free blocks in case of critically-low memory condition
|
||||||
freeBlocks.fastRemove();
|
PendingFreeBlock* temp = (PendingFreeBlock *)((char *)freeBlocks.getAddErrorValue().block+
|
||||||
} else {
|
ALIGN(sizeof(MemoryBlock)));
|
||||||
// If we are in a critically-low memory condition our block could be in the
|
temp->next = pendingFree;
|
||||||
// pending free blocks list. Remove it from there
|
pendingFree = temp;
|
||||||
PendingFreeBlock *itr = pendingFree, *temp = (PendingFreeBlock *)(prev+1);
|
}
|
||||||
if (itr == temp)
|
}
|
||||||
pendingFree = itr->next;
|
|
||||||
else
|
void MemoryPool::removeFreeBlock(MemoryBlock *blk) {
|
||||||
{
|
BlockInfo info = {blk, blk->length};
|
||||||
while ( itr ) {
|
if (freeBlocks.locate(info)) {
|
||||||
PendingFreeBlock *next = itr->next;
|
freeBlocks.fastRemove();
|
||||||
if (next==temp) {
|
} else {
|
||||||
itr->next = temp->next;
|
// If we are in a critically-low memory condition our block could be in the
|
||||||
break;
|
// pending free blocks list. Remove it from there
|
||||||
}
|
PendingFreeBlock *itr = pendingFree,
|
||||||
itr = next;
|
*temp = (PendingFreeBlock *)((char *)blk+ALIGN(sizeof(MemoryBlock)));
|
||||||
|
if (itr == temp)
|
||||||
|
pendingFree = itr->next;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while ( itr ) {
|
||||||
|
PendingFreeBlock *next = itr->next;
|
||||||
|
if (next==temp) {
|
||||||
|
itr->next = temp->next;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
itr = next;
|
||||||
}
|
}
|
||||||
assert(itr); // We had to find it somewhere
|
assert(itr); // We had to find it somewhere
|
||||||
}
|
}
|
||||||
prev->length += blk->length + ALIGN(sizeof(MemoryBlock));
|
|
||||||
prev->last = blk->last;
|
|
||||||
if (!blk->last)
|
|
||||||
((MemoryBlock *)((char *)blk+ALIGN(sizeof(MemoryBlock))+blk->length))->prev = prev;
|
|
||||||
temp.length = prev->length;
|
|
||||||
internalAlloc = true;
|
|
||||||
try {
|
|
||||||
freeBlocks.add(temp);
|
|
||||||
} catch(...) {
|
|
||||||
// Add item to the list of pending free blocks in case of critically-low memory condition
|
|
||||||
PendingFreeBlock* temp = (PendingFreeBlock *)(freeBlocks.getAddErrorValue().block+1);
|
|
||||||
temp->next = pendingFree;
|
|
||||||
pendingFree = temp;
|
|
||||||
}
|
|
||||||
internalAlloc = false;
|
|
||||||
} else {
|
|
||||||
// Try to merge block with next free block
|
|
||||||
if (!blk->last) {
|
|
||||||
MemoryBlock *next = (MemoryBlock *)((char*)blk+ALIGN(sizeof(MemoryBlock))+blk->length);
|
|
||||||
if (!next->used) {
|
|
||||||
blk->length += next->length + ALIGN(sizeof(MemoryBlock));
|
|
||||||
blk->last = next->last;
|
|
||||||
if (!next->last)
|
|
||||||
((MemoryBlock *)((char *)next+ALIGN(sizeof(MemoryBlock))+next->length))->prev = blk;
|
|
||||||
BlockInfo temp = {next, next->length};
|
|
||||||
if (freeBlocks.locate(temp)) {
|
|
||||||
freeBlocks.fastRemove();
|
|
||||||
} else {
|
|
||||||
// If we are in a critically-low memory condition our block could be in the
|
|
||||||
// pending free blocks list. Remove it from there
|
|
||||||
PendingFreeBlock *itr = pendingFree,
|
|
||||||
*temp = (PendingFreeBlock *)((char*)prev+ALIGN(sizeof(MemoryBlock)));
|
|
||||||
if (itr == temp)
|
|
||||||
pendingFree = itr->next;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while ( itr ) {
|
|
||||||
PendingFreeBlock *next = itr->next;
|
|
||||||
if (next==temp) {
|
|
||||||
itr->next = temp->next;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
itr = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(itr); // We had to find it somewhere
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Mark block as free and add it to freeBlocks array
|
|
||||||
blk->used = false;
|
|
||||||
BlockInfo temp = {blk, blk->length};
|
|
||||||
internalAlloc = true;
|
|
||||||
try {
|
|
||||||
freeBlocks.add(temp);
|
|
||||||
} catch(...) {
|
|
||||||
// Add item to the list of pending free blocks in case of critically-low memory condition
|
|
||||||
PendingFreeBlock* temp = (PendingFreeBlock *)(
|
|
||||||
(char*)freeBlocks.getAddErrorValue().block+ALIGN(sizeof(MemoryBlock)));
|
|
||||||
temp->next = pendingFree;
|
|
||||||
pendingFree = temp;
|
|
||||||
}
|
|
||||||
internalAlloc = false;
|
|
||||||
}
|
|
||||||
// Grow spare blocks pool if necessary
|
|
||||||
if (needSpare) {
|
|
||||||
updateSpareBlocks: {
|
|
||||||
try {
|
|
||||||
if (!spareLeaf)
|
|
||||||
spareLeaf = alloc(sizeof(FreeBlocksTree::ItemList), TYPE_LEAFPAGE);
|
|
||||||
while (spareNodes.getCount() <= freeBlocks.level)
|
|
||||||
spareNodes.add(alloc(sizeof(FreeBlocksTree::NodeList), TYPE_TREEPAGE));
|
|
||||||
needSpare = false;
|
|
||||||
// Great, if we were able to restore free blocks tree operations after critically low
|
|
||||||
// memory condition then try to add pending free blocks to our tree
|
|
||||||
while (pendingFree) {
|
|
||||||
PendingFreeBlock *temp = pendingFree;
|
|
||||||
pendingFree = temp->next;
|
|
||||||
BlockInfo info = {
|
|
||||||
(MemoryBlock*)((char*)temp-ALIGN(sizeof(MemoryBlock))),
|
|
||||||
((MemoryBlock*)temp)->length
|
|
||||||
};
|
|
||||||
internalAlloc = true;
|
|
||||||
freeBlocks.add(info); // We should be able to do this because we had spare blocks
|
|
||||||
internalAlloc = false;
|
|
||||||
if (needSpare)
|
|
||||||
goto updateSpareBlocks;
|
|
||||||
}
|
|
||||||
} catch(...) {
|
|
||||||
// We can recover after this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MemoryPool::free(void *block) {
|
||||||
|
if (internalAlloc) {
|
||||||
|
((PendingFreeBlock*)block)->next = pendingFree;
|
||||||
|
((MemoryBlock*)((char*)block-ALIGN(sizeof(MemoryBlock))))->used = false;
|
||||||
|
pendingFree = (PendingFreeBlock*)block;
|
||||||
|
needSpare = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
internalAlloc = true;
|
||||||
|
MemoryBlock *blk = (MemoryBlock *)((char*)block - ALIGN(sizeof(MemoryBlock))), *prev;
|
||||||
|
// Try to merge block with preceding free block
|
||||||
|
if ((prev = blk->prev) && !prev->used) {
|
||||||
|
removeFreeBlock(prev);
|
||||||
|
prev->length += blk->length + ALIGN(sizeof(MemoryBlock));
|
||||||
|
|
||||||
|
MemoryBlock *next = NULL;
|
||||||
|
if (blk->last) {
|
||||||
|
prev->last = true;
|
||||||
|
} else {
|
||||||
|
next = (MemoryBlock *)((char *)blk+ALIGN(sizeof(MemoryBlock))+blk->length);
|
||||||
|
if (next->used) {
|
||||||
|
next->prev = prev;
|
||||||
|
prev->last = false;
|
||||||
|
} else {
|
||||||
|
// Merge next block too
|
||||||
|
removeFreeBlock(next);
|
||||||
|
prev->length += next->length + ALIGN(sizeof(MemoryBlock));
|
||||||
|
prev->last = next->last;
|
||||||
|
if (!next->last)
|
||||||
|
((MemoryBlock *)((char *)next+ALIGN(sizeof(MemoryBlock))+next->length))->prev = prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addFreeBlock(prev);
|
||||||
|
} else {
|
||||||
|
MemoryBlock *next;
|
||||||
|
// Mark block as free
|
||||||
|
blk->used = false;
|
||||||
|
// Try to merge block with next free block
|
||||||
|
if (!blk->last &&
|
||||||
|
!(next = (MemoryBlock *)((char*)blk+ALIGN(sizeof(MemoryBlock))+blk->length))->used)
|
||||||
|
{
|
||||||
|
removeFreeBlock(next);
|
||||||
|
blk->length += next->length + ALIGN(sizeof(MemoryBlock));
|
||||||
|
blk->last = next->last;
|
||||||
|
if (!next->last)
|
||||||
|
((MemoryBlock *)((char *)next+ALIGN(sizeof(MemoryBlock))+next->length))->prev = blk;
|
||||||
|
}
|
||||||
|
addFreeBlock(blk);
|
||||||
|
}
|
||||||
|
internalAlloc = false;
|
||||||
|
if (needSpare && !updatingSpare) updateSpare();
|
||||||
|
}
|
||||||
|
|
||||||
} /* namespace Firebird */
|
} /* namespace Firebird */
|
||||||
|
@ -79,19 +79,20 @@ private:
|
|||||||
FreeBlocksTree freeBlocks; // B+ tree ordered by (length,address)
|
FreeBlocksTree freeBlocks; // B+ tree ordered by (length,address)
|
||||||
MemoryExtent *extents; // Linked list of all memory extents
|
MemoryExtent *extents; // Linked list of all memory extents
|
||||||
|
|
||||||
void* spareLeaf;
|
Vector<void*,2> spareLeafs;
|
||||||
|
Vector<void*,MAX_TREE_DEPTH+1> spareNodes;
|
||||||
bool internalAlloc;
|
bool internalAlloc;
|
||||||
bool needSpare;
|
bool needSpare;
|
||||||
Vector<void*,MAX_TREE_DEPTH> spareNodes;
|
bool updatingSpare;
|
||||||
PendingFreeBlock *pendingFree;
|
PendingFreeBlock *pendingFree;
|
||||||
|
|
||||||
// Do not allow to create and destroy pool directly from outside
|
// Do not allow to create and destroy pool directly from outside
|
||||||
MemoryPool(void *first_extent, void *root_page) :
|
MemoryPool(void *first_extent, void *root_page) :
|
||||||
freeBlocks(this, root_page),
|
freeBlocks(this, root_page),
|
||||||
extents((MemoryExtent *)first_extent),
|
extents((MemoryExtent *)first_extent),
|
||||||
spareLeaf(NULL),
|
|
||||||
internalAlloc(false),
|
internalAlloc(false),
|
||||||
needSpare(false),
|
needSpare(false),
|
||||||
|
updatingSpare(false),
|
||||||
pendingFree(NULL)
|
pendingFree(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -104,6 +105,12 @@ private:
|
|||||||
static void* external_alloc(size_t size);
|
static void* external_alloc(size_t size);
|
||||||
|
|
||||||
static void external_free(void *blk);
|
static void external_free(void *blk);
|
||||||
|
|
||||||
|
void updateSpare();
|
||||||
|
|
||||||
|
void addFreeBlock(MemoryBlock *blk);
|
||||||
|
|
||||||
|
void removeFreeBlock(MemoryBlock *blk);
|
||||||
public:
|
public:
|
||||||
static MemoryPool* createPool();
|
static MemoryPool* createPool();
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
#include "alloc.h"
|
||||||
|
#include "../memory/memory_pool.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -38,12 +40,13 @@ void start() {
|
|||||||
|
|
||||||
void report(int scale) {
|
void report(int scale) {
|
||||||
clock_t d = clock();
|
clock_t d = clock();
|
||||||
printf("Add+remove %d elements from array of scale %d took %d milliseconds. \n",
|
printf("Add+remove %d elements from tree of scale %d took %d milliseconds. \n",
|
||||||
TEST_ITEMS, scale, (int)(d-t)*1000/CLOCKS_PER_SEC);
|
TEST_ITEMS, scale, (int)(d-t)*1000/CLOCKS_PER_SEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
using namespace Firebird;
|
||||||
using namespace Firebird;
|
|
||||||
|
static void testTree() {
|
||||||
printf("Fill array with test data (%d items)...", TEST_ITEMS);
|
printf("Fill array with test data (%d items)...", TEST_ITEMS);
|
||||||
Vector<int, TEST_ITEMS> *v = new Vector<int, TEST_ITEMS>();
|
Vector<int, TEST_ITEMS> *v = new Vector<int, TEST_ITEMS>();
|
||||||
int n = 0;
|
int n = 0;
|
||||||
@ -142,3 +145,178 @@ int main() {
|
|||||||
printf("Just a reference: add+remove %d elements from STL tree took %d milliseconds. \n",
|
printf("Just a reference: add+remove %d elements from STL tree took %d milliseconds. \n",
|
||||||
TEST_ITEMS, (int)(d-t)*1000/CLOCKS_PER_SEC);
|
TEST_ITEMS, (int)(d-t)*1000/CLOCKS_PER_SEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void report() {
|
||||||
|
clock_t d = clock();
|
||||||
|
printf("Operation took %d milliseconds.\n", (int)(d-t)*1000/CLOCKS_PER_SEC);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ALLOC_ITEMS 1000000
|
||||||
|
#define MAX_ITEM_SIZE 50
|
||||||
|
#define BIG_ITEMS (ALLOC_ITEMS/10)
|
||||||
|
#define BIG_SIZE (MAX_ITEM_SIZE*5)
|
||||||
|
|
||||||
|
struct AllocItem {
|
||||||
|
int order;
|
||||||
|
void *item;
|
||||||
|
static int compare(const AllocItem &i1, const AllocItem &i2) {
|
||||||
|
return i1.order > i2.order || (i1.order==i2.order && i1.item > i2.item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void testAllocatorOverhead() {
|
||||||
|
printf("Calculating measurement overhead...\n");
|
||||||
|
start();
|
||||||
|
MallocAllocator allocator;
|
||||||
|
BePlusTree<AllocItem,AllocItem,MallocAllocator,DefaultKeyValue<AllocItem>,AllocItem> items(&allocator),
|
||||||
|
bigItems(&allocator);
|
||||||
|
// Allocate small items
|
||||||
|
int n = 0;
|
||||||
|
for (int i=0;i<ALLOC_ITEMS;i++) {
|
||||||
|
n = n * 47163 - 57412;
|
||||||
|
AllocItem temp = {n, (void*)i};
|
||||||
|
items.add(temp);
|
||||||
|
}
|
||||||
|
// Deallocate half of small items
|
||||||
|
n = 0;
|
||||||
|
if (items.getFirst()) do {
|
||||||
|
items.current();
|
||||||
|
n++;
|
||||||
|
} while (n < ALLOC_ITEMS/2 && items.getNext());
|
||||||
|
// Allocate big items
|
||||||
|
for (int i=0;i<BIG_ITEMS;i++) {
|
||||||
|
n = n * 47163 - 57412;
|
||||||
|
AllocItem temp = {n, (void*)i};
|
||||||
|
bigItems.add(temp);
|
||||||
|
}
|
||||||
|
// Deallocate the rest of small items
|
||||||
|
do {
|
||||||
|
items.current();
|
||||||
|
} while (items.getNext());
|
||||||
|
// Deallocate big items
|
||||||
|
if (bigItems.getFirst()) do {
|
||||||
|
bigItems.current();
|
||||||
|
} while (bigItems.getNext());
|
||||||
|
report();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testAllocatorMemoryPool() {
|
||||||
|
printf("Test run for Firebird::MemoryPool...\n");
|
||||||
|
start();
|
||||||
|
Firebird::MemoryPool* pool = Firebird::MemoryPool::createPool();
|
||||||
|
MallocAllocator allocator;
|
||||||
|
BePlusTree<AllocItem,AllocItem,MallocAllocator,DefaultKeyValue<AllocItem>,AllocItem> items(&allocator),
|
||||||
|
bigItems(&allocator);
|
||||||
|
// Allocate small items
|
||||||
|
int n = 0;
|
||||||
|
for (int i=0;i<ALLOC_ITEMS;i++) {
|
||||||
|
n = n * 47163 - 57412;
|
||||||
|
AllocItem temp = {n, pool->alloc((n % MAX_ITEM_SIZE + MAX_ITEM_SIZE)/2+1)};
|
||||||
|
items.add(temp);
|
||||||
|
}
|
||||||
|
// Deallocate half of small items
|
||||||
|
n = 0;
|
||||||
|
if (items.getFirst()) do {
|
||||||
|
pool->free(items.current().item);
|
||||||
|
n++;
|
||||||
|
} while (n < ALLOC_ITEMS/2 && items.getNext());
|
||||||
|
// Allocate big items
|
||||||
|
for (int i=0;i<BIG_ITEMS;i++) {
|
||||||
|
n = n * 47163 - 57412;
|
||||||
|
AllocItem temp = {n, pool->alloc((n % BIG_SIZE + BIG_SIZE)/2+1)};
|
||||||
|
bigItems.add(temp);
|
||||||
|
}
|
||||||
|
// Deallocate the rest of small items
|
||||||
|
do {
|
||||||
|
pool->free(items.current().item);
|
||||||
|
} while (items.getNext());
|
||||||
|
// Deallocate big items
|
||||||
|
if (bigItems.getFirst()) do {
|
||||||
|
pool->free(bigItems.current().item);
|
||||||
|
} while (bigItems.getNext());
|
||||||
|
Firebird::MemoryPool::deletePool(pool);
|
||||||
|
report();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testAllocatorMalloc() {
|
||||||
|
printf("Test reference run for ::malloc...\n");
|
||||||
|
start();
|
||||||
|
MallocAllocator allocator;
|
||||||
|
BePlusTree<AllocItem,AllocItem,MallocAllocator,DefaultKeyValue<AllocItem>,AllocItem> items(&allocator),
|
||||||
|
bigItems(&allocator);
|
||||||
|
// Allocate small items
|
||||||
|
int n = 0;
|
||||||
|
for (int i=0;i<ALLOC_ITEMS;i++) {
|
||||||
|
n = n * 47163 - 57412;
|
||||||
|
AllocItem temp = {n, malloc((n % MAX_ITEM_SIZE + MAX_ITEM_SIZE)/2+1)};
|
||||||
|
items.add(temp);
|
||||||
|
}
|
||||||
|
// Deallocate half of small items
|
||||||
|
n = 0;
|
||||||
|
if (items.getFirst()) do {
|
||||||
|
free(items.current().item);
|
||||||
|
n++;
|
||||||
|
} while (n < ALLOC_ITEMS/2 && items.getNext());
|
||||||
|
// Allocate big items
|
||||||
|
for (int i=0;i<BIG_ITEMS;i++) {
|
||||||
|
n = n * 47163 - 57412;
|
||||||
|
AllocItem temp = {n, malloc((n % BIG_SIZE + BIG_SIZE)/2+1)};
|
||||||
|
bigItems.add(temp);
|
||||||
|
}
|
||||||
|
// Deallocate the rest of small items
|
||||||
|
do {
|
||||||
|
free(items.current().item);
|
||||||
|
} while (items.getNext());
|
||||||
|
// Deallocate big items
|
||||||
|
if (bigItems.getFirst()) do {
|
||||||
|
free(bigItems.current().item);
|
||||||
|
} while (bigItems.getNext());
|
||||||
|
report();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testAllocatorOldPool() {
|
||||||
|
printf("Test run for old MemoryPool...\n");
|
||||||
|
start();
|
||||||
|
::MemoryPool *pool = new ::MemoryPool(0,getDefaultMemoryPool());
|
||||||
|
MallocAllocator allocator;
|
||||||
|
BePlusTree<AllocItem,AllocItem,MallocAllocator,DefaultKeyValue<AllocItem>,AllocItem> items(&allocator),
|
||||||
|
bigItems(&allocator);
|
||||||
|
// Allocate small items
|
||||||
|
int n = 0;
|
||||||
|
for (int i=0;i<ALLOC_ITEMS;i++) {
|
||||||
|
n = n * 47163 - 57412;
|
||||||
|
AllocItem temp = {n, pool->allocate((n % MAX_ITEM_SIZE + MAX_ITEM_SIZE)/2+1,0)};
|
||||||
|
items.add(temp);
|
||||||
|
}
|
||||||
|
// Deallocate half of small items
|
||||||
|
n = 0;
|
||||||
|
if (items.getFirst()) do {
|
||||||
|
pool->deallocate(items.current().item);
|
||||||
|
n++;
|
||||||
|
} while (n < ALLOC_ITEMS/2 && items.getNext());
|
||||||
|
// Allocate big items
|
||||||
|
for (int i=0;i<BIG_ITEMS;i++) {
|
||||||
|
n = n * 47163 - 57412;
|
||||||
|
AllocItem temp = {n, pool->allocate((n % BIG_SIZE + BIG_SIZE)/2+1,0)};
|
||||||
|
bigItems.add(temp);
|
||||||
|
}
|
||||||
|
/* // Deallocate the rest of small items
|
||||||
|
do {
|
||||||
|
pool->deallocate(items.current().item);
|
||||||
|
} while (items.getNext());*/
|
||||||
|
// Deallocate big items
|
||||||
|
if (bigItems.getFirst()) do {
|
||||||
|
pool->deallocate(bigItems.current().item);
|
||||||
|
} while (bigItems.getNext());
|
||||||
|
delete pool;
|
||||||
|
report();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
testTree();
|
||||||
|
testAllocatorOverhead();
|
||||||
|
testAllocatorMemoryPool();
|
||||||
|
testAllocatorMalloc();
|
||||||
|
testAllocatorOldPool();
|
||||||
|
}
|
||||||
|
@ -55,7 +55,7 @@ void testSortedVector() {
|
|||||||
printf(passed?"PASSED\n":"FAILED\n");
|
printf(passed?"PASSED\n":"FAILED\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TEST_ITEMS 100000
|
#define TEST_ITEMS 1000000
|
||||||
|
|
||||||
struct Test {
|
struct Test {
|
||||||
int value;
|
int value;
|
||||||
@ -244,8 +244,10 @@ void testBePlusTree() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ALLOC_ITEMS 10
|
#define ALLOC_ITEMS 1000000
|
||||||
#define MAX_ITEM_SIZE 100
|
#define MAX_ITEM_SIZE 50
|
||||||
|
#define BIG_ITEMS (ALLOC_ITEMS/10)
|
||||||
|
#define BIG_SIZE (MAX_ITEM_SIZE*5)
|
||||||
|
|
||||||
struct AllocItem {
|
struct AllocItem {
|
||||||
int order;
|
int order;
|
||||||
@ -260,7 +262,8 @@ void testAllocator() {
|
|||||||
MemoryPool* pool = MemoryPool::createPool();
|
MemoryPool* pool = MemoryPool::createPool();
|
||||||
|
|
||||||
MallocAllocator allocator;
|
MallocAllocator allocator;
|
||||||
BePlusTree<AllocItem,AllocItem,MallocAllocator,DefaultKeyValue<AllocItem>,AllocItem> items(&allocator);
|
BePlusTree<AllocItem,AllocItem,MallocAllocator,DefaultKeyValue<AllocItem>,AllocItem> items(&allocator),
|
||||||
|
bigItems(&allocator);
|
||||||
printf("Allocate %d items: ", ALLOC_ITEMS);
|
printf("Allocate %d items: ", ALLOC_ITEMS);
|
||||||
int n = 0;
|
int n = 0;
|
||||||
pool->verify_pool();
|
pool->verify_pool();
|
||||||
@ -268,18 +271,44 @@ void testAllocator() {
|
|||||||
n = n * 47163 - 57412;
|
n = n * 47163 - 57412;
|
||||||
AllocItem temp = {n, pool->alloc((n % MAX_ITEM_SIZE + MAX_ITEM_SIZE)/2+1)};
|
AllocItem temp = {n, pool->alloc((n % MAX_ITEM_SIZE + MAX_ITEM_SIZE)/2+1)};
|
||||||
items.add(temp);
|
items.add(temp);
|
||||||
pool->verify_pool();
|
|
||||||
}
|
}
|
||||||
printf(" DONE\n");
|
printf(" DONE\n");
|
||||||
|
pool->verify_pool();
|
||||||
|
|
||||||
printf("Deallocate items in quasi-random order: ");
|
printf("Deallocate half of items in quasi-random order: ");
|
||||||
|
n = 0;
|
||||||
if (items.getFirst()) do {
|
if (items.getFirst()) do {
|
||||||
pool->free(items.current().item);
|
pool->free(items.current().item);
|
||||||
pool->verify_pool();
|
n++;
|
||||||
|
} while (n < ALLOC_ITEMS/2 && items.getNext());
|
||||||
|
printf(" DONE\n");
|
||||||
|
pool->verify_pool();
|
||||||
|
|
||||||
|
printf("Allocate %d big items: ", BIG_ITEMS);
|
||||||
|
n = 0;
|
||||||
|
pool->verify_pool();
|
||||||
|
for (int i=0;i<BIG_ITEMS;i++) {
|
||||||
|
n = n * 47163 - 57412;
|
||||||
|
AllocItem temp = {n, pool->alloc((n % BIG_SIZE + BIG_SIZE)/2+1)};
|
||||||
|
bigItems.add(temp);
|
||||||
|
}
|
||||||
|
printf(" DONE\n");
|
||||||
|
pool->verify_pool();
|
||||||
|
|
||||||
|
printf("Deallocate the rest of small items in quasi-random order: ");
|
||||||
|
do {
|
||||||
|
pool->free(items.current().item);
|
||||||
} while (items.getNext());
|
} while (items.getNext());
|
||||||
printf(" DONE\n");
|
printf(" DONE\n");
|
||||||
|
pool->verify_pool();
|
||||||
|
|
||||||
|
printf("Deallocate big items in quasi-random order: ");
|
||||||
|
if (bigItems.getFirst()) do {
|
||||||
|
pool->free(bigItems.current().item);
|
||||||
|
} while (bigItems.getNext());
|
||||||
|
printf(" DONE\n");
|
||||||
|
pool->verify_pool();
|
||||||
// TODO:
|
// TODO:
|
||||||
// printf("Check that pool contains one free block per each segment and blocks have correct sizes");
|
|
||||||
// Test critically low memory conditions
|
// Test critically low memory conditions
|
||||||
MemoryPool::deletePool(pool);
|
MemoryPool::deletePool(pool);
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,8 @@ g++ -ggdb -Wall class_test.cpp alloc.cpp 2> aa
|
|||||||
./a.out
|
./a.out
|
||||||
|
|
||||||
# Chose the best algorithm parameters for the target architecture
|
# Chose the best algorithm parameters for the target architecture
|
||||||
g++ -O3 -march=pentium3 -DNDEBUG class_perf.cpp
|
g++ -O3 -march=pentium3 -DNDEBUG -I../../include class_perf.cpp alloc.cpp \
|
||||||
|
../memory/memory_pool.cpp ../fb_exception.cpp ../memory/allocators.cpp 2> aa
|
||||||
|
#g++ -ggdb -I../../include class_perf.cpp alloc.cpp \
|
||||||
|
#../memory/memory_pool.cpp ../fb_exception.cpp ../memory/allocators.cpp 2> aa
|
||||||
./a.out
|
./a.out
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
|
// FIXME: Temporary until we switch out of using STL
|
||||||
|
#include <new>
|
||||||
|
|
||||||
// This macro controls merging of nodes of all B+ trees
|
// This macro controls merging of nodes of all B+ trees
|
||||||
// Now it merges pages only when resulting page will be 3/4 filled or less
|
// Now it merges pages only when resulting page will be 3/4 filled or less
|
||||||
@ -57,9 +59,9 @@ public:
|
|||||||
|
|
||||||
enum LocType { locEqual, locLess, locGreat, locGreatEqual, locLessEqual };
|
enum LocType { locEqual, locLess, locGreat, locGreatEqual, locLessEqual };
|
||||||
|
|
||||||
inline void* operator new (size_t size, void *place) {
|
/*inline void* operator new (size_t size, void *place) {
|
||||||
return place;
|
return place;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// Fast and simple B+ tree of simple types
|
// Fast and simple B+ tree of simple types
|
||||||
// Tree has state (current item) and no iterator classes.
|
// Tree has state (current item) and no iterator classes.
|
||||||
@ -286,10 +288,6 @@ private:
|
|||||||
template <typename Value, typename Key, typename Allocator, typename KeyOfValue, typename Cmp, int LeafCount, int NodeCount>
|
template <typename Value, typename Key, typename Allocator, typename KeyOfValue, typename Cmp, int LeafCount, int NodeCount>
|
||||||
void BePlusTree<Value, Key, Allocator, KeyOfValue, Cmp, LeafCount, NodeCount>::_removePage(int nodeLevel, void *node)
|
void BePlusTree<Value, Key, Allocator, KeyOfValue, Cmp, LeafCount, NodeCount>::_removePage(int nodeLevel, void *node)
|
||||||
{
|
{
|
||||||
// We need to defer all deallocations until we finish manipulating the
|
|
||||||
// tree to enable re-enterance. That is why we use recursion instead
|
|
||||||
// of loops to climb up the tree. This doesn't hurt performance in any
|
|
||||||
// way but makes allocator implementation simplier
|
|
||||||
NodeList *list;
|
NodeList *list;
|
||||||
// Get parent and adjust the links
|
// Get parent and adjust the links
|
||||||
if (nodeLevel) {
|
if (nodeLevel) {
|
||||||
@ -612,11 +610,9 @@ bool BePlusTree<Value, Key, Allocator, KeyOfValue, Cmp, LeafCount, NodeCount>::a
|
|||||||
newNode = lower;
|
newNode = lower;
|
||||||
curLevel--;
|
curLevel--;
|
||||||
}
|
}
|
||||||
Value temp = (*(ItemList*)newNode)[0];
|
addErrorValue = (*(ItemList*)newNode)[0];
|
||||||
((ItemList *)newNode)->~ItemList();
|
((ItemList *)newNode)->~ItemList();
|
||||||
pool->free(newNode);
|
pool->free(newNode);
|
||||||
addErrorValue = temp; // We can do this only after all deallocations to prevent problems
|
|
||||||
// caused by re-enterance from allocator
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
Loading…
Reference in New Issue
Block a user