mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 00:03: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))
|
||||
|
||||
// TODO:
|
||||
// 1. red zones checking
|
||||
// 2. alloc/free pattern
|
||||
// TODO (in order of importance):
|
||||
// 1. Pool size limit
|
||||
// 2. debug alloc/free pattern
|
||||
// 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 {
|
||||
|
||||
@ -50,8 +52,42 @@ static void pool_out_of_memory()
|
||||
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) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -156,9 +192,7 @@ MemoryPool* MemoryPool::createPool() {
|
||||
blk->prev = hdr;
|
||||
BlockInfo temp = {blk, blockLength};
|
||||
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->spareLeaf = pool->alloc(sizeof(FreeBlocksTree::ItemList));
|
||||
pool->spareNodes.add(pool->alloc(sizeof(FreeBlocksTree::NodeList)));
|
||||
pool->updateSpare();
|
||||
return pool;
|
||||
}
|
||||
|
||||
@ -176,11 +210,13 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
||||
if (internalAlloc) {
|
||||
if (size == sizeof(FreeBlocksTree::ItemList))
|
||||
// This condition is to handle case when nodelist and itemlist have equal size
|
||||
if (sizeof(FreeBlocksTree::ItemList)!=sizeof(FreeBlocksTree::ItemList) || spareLeaf) {
|
||||
void *temp = spareLeaf;
|
||||
spareLeaf = NULL;
|
||||
if (sizeof(FreeBlocksTree::ItemList)!=sizeof(FreeBlocksTree::NodeList) ||
|
||||
spareLeafs.getCount())
|
||||
{
|
||||
if (!spareLeafs.getCount()) pool_out_of_memory();
|
||||
void *temp = spareLeafs[spareLeafs.getCount()-1];
|
||||
spareLeafs.shrink(spareLeafs.getCount()-1);
|
||||
needSpare = true;
|
||||
if (!temp) pool_out_of_memory();
|
||||
return temp;
|
||||
}
|
||||
if (size == sizeof(FreeBlocksTree::NodeList)) {
|
||||
@ -204,7 +240,9 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
||||
current->block->used = true;
|
||||
current->block->type = type;
|
||||
result = (char *)current->block + ALIGN(sizeof(MemoryBlock));
|
||||
internalAlloc = true;
|
||||
freeBlocks.fastRemove();
|
||||
internalAlloc = false;
|
||||
} else {
|
||||
// Cut a piece at the end of block in hope to avoid structural
|
||||
// modification of free blocks tree
|
||||
@ -221,7 +259,7 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
||||
if (!blk->last)
|
||||
((MemoryBlock *)((char*)blk + ALIGN(sizeof(MemoryBlock)) + blk->length))->prev = blk;
|
||||
// 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;
|
||||
else {
|
||||
// Tree needs to be modified structurally
|
||||
@ -230,23 +268,15 @@ void* MemoryPool::alloc(size_t size, SSHORT type) {
|
||||
#else
|
||||
bool res = freeBlocks.getNext();
|
||||
assert(res);
|
||||
assert(&freeBlocks.current()==current);
|
||||
#endif
|
||||
BlockInfo temp = {current->block, current->block->length};
|
||||
freeBlocks.fastRemove();
|
||||
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;
|
||||
}
|
||||
MemoryBlock *block = current->block;
|
||||
freeBlocks.fastRemove();
|
||||
addFreeBlock(block);
|
||||
internalAlloc = false;
|
||||
}
|
||||
result = blk+1;
|
||||
result = (char*)blk+ALIGN(sizeof(MemoryBlock));
|
||||
}
|
||||
} else {
|
||||
// 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)
|
||||
prev->next = itr->next;
|
||||
else
|
||||
pendingFree = itr->next;
|
||||
pendingFree = itr->next;
|
||||
return itr;
|
||||
} else {
|
||||
// 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)) -
|
||||
ALIGN(sizeof(MemoryBlock)) - size - ALIGN(sizeof(MemoryBlock));
|
||||
rest->prev = blk;
|
||||
BlockInfo temp = {rest, rest->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;
|
||||
}
|
||||
addFreeBlock(rest);
|
||||
internalAlloc = false;
|
||||
}
|
||||
result = (char*)blk+ALIGN(sizeof(MemoryBlock));
|
||||
}
|
||||
// Grow spare blocks pool if necessary
|
||||
if (needSpare) {
|
||||
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
|
||||
}
|
||||
}
|
||||
if (needSpare && !updatingSpare) updateSpare();
|
||||
return result;
|
||||
}
|
||||
|
||||
void MemoryPool::free(void *block) {
|
||||
MemoryBlock *blk = (MemoryBlock *)((char*)block - ALIGN(sizeof(MemoryBlock))), *prev;
|
||||
// Try to merge block with preceding free block
|
||||
if ((prev = blk->prev) && !prev->used) {
|
||||
BlockInfo temp = {prev, prev->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 *)(prev+1);
|
||||
if (itr == temp)
|
||||
pendingFree = itr->next;
|
||||
else
|
||||
{
|
||||
while ( itr ) {
|
||||
PendingFreeBlock *next = itr->next;
|
||||
if (next==temp) {
|
||||
itr->next = temp->next;
|
||||
break;
|
||||
}
|
||||
itr = next;
|
||||
void MemoryPool::addFreeBlock(MemoryBlock *blk) {
|
||||
BlockInfo info = {blk, blk->length};
|
||||
try {
|
||||
freeBlocks.add(info);
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryPool::removeFreeBlock(MemoryBlock *blk) {
|
||||
BlockInfo info = {blk, blk->length};
|
||||
if (freeBlocks.locate(info)) {
|
||||
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 *)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
|
||||
}
|
||||
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 */
|
||||
|
@ -79,19 +79,20 @@ private:
|
||||
FreeBlocksTree freeBlocks; // B+ tree ordered by (length,address)
|
||||
MemoryExtent *extents; // Linked list of all memory extents
|
||||
|
||||
void* spareLeaf;
|
||||
Vector<void*,2> spareLeafs;
|
||||
Vector<void*,MAX_TREE_DEPTH+1> spareNodes;
|
||||
bool internalAlloc;
|
||||
bool needSpare;
|
||||
Vector<void*,MAX_TREE_DEPTH> spareNodes;
|
||||
bool updatingSpare;
|
||||
PendingFreeBlock *pendingFree;
|
||||
|
||||
// Do not allow to create and destroy pool directly from outside
|
||||
MemoryPool(void *first_extent, void *root_page) :
|
||||
freeBlocks(this, root_page),
|
||||
extents((MemoryExtent *)first_extent),
|
||||
spareLeaf(NULL),
|
||||
internalAlloc(false),
|
||||
needSpare(false),
|
||||
updatingSpare(false),
|
||||
pendingFree(NULL)
|
||||
{
|
||||
}
|
||||
@ -104,6 +105,12 @@ private:
|
||||
static void* external_alloc(size_t size);
|
||||
|
||||
static void external_free(void *blk);
|
||||
|
||||
void updateSpare();
|
||||
|
||||
void addFreeBlock(MemoryBlock *blk);
|
||||
|
||||
void removeFreeBlock(MemoryBlock *blk);
|
||||
public:
|
||||
static MemoryPool* createPool();
|
||||
|
||||
|
@ -24,6 +24,8 @@
|
||||
*/
|
||||
|
||||
#include "tree.h"
|
||||
#include "alloc.h"
|
||||
#include "../memory/memory_pool.h"
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <set>
|
||||
@ -38,12 +40,13 @@ void start() {
|
||||
|
||||
void report(int scale) {
|
||||
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);
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace Firebird;
|
||||
using namespace Firebird;
|
||||
|
||||
static void testTree() {
|
||||
printf("Fill array with test data (%d items)...", TEST_ITEMS);
|
||||
Vector<int, TEST_ITEMS> *v = new Vector<int, TEST_ITEMS>();
|
||||
int n = 0;
|
||||
@ -142,3 +145,178 @@ int main() {
|
||||
printf("Just a reference: add+remove %d elements from STL tree took %d milliseconds. \n",
|
||||
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");
|
||||
}
|
||||
|
||||
#define TEST_ITEMS 100000
|
||||
#define TEST_ITEMS 1000000
|
||||
|
||||
struct Test {
|
||||
int value;
|
||||
@ -244,8 +244,10 @@ void testBePlusTree() {
|
||||
|
||||
}
|
||||
|
||||
#define ALLOC_ITEMS 10
|
||||
#define MAX_ITEM_SIZE 100
|
||||
#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;
|
||||
@ -260,7 +262,8 @@ void testAllocator() {
|
||||
MemoryPool* pool = MemoryPool::createPool();
|
||||
|
||||
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);
|
||||
int n = 0;
|
||||
pool->verify_pool();
|
||||
@ -268,18 +271,44 @@ void testAllocator() {
|
||||
n = n * 47163 - 57412;
|
||||
AllocItem temp = {n, pool->alloc((n % MAX_ITEM_SIZE + MAX_ITEM_SIZE)/2+1)};
|
||||
items.add(temp);
|
||||
pool->verify_pool();
|
||||
}
|
||||
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 {
|
||||
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());
|
||||
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:
|
||||
// printf("Check that pool contains one free block per each segment and blocks have correct sizes");
|
||||
// Test critically low memory conditions
|
||||
MemoryPool::deletePool(pool);
|
||||
}
|
||||
|
@ -5,5 +5,8 @@ g++ -ggdb -Wall class_test.cpp alloc.cpp 2> aa
|
||||
./a.out
|
||||
|
||||
# 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
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include <string.h>
|
||||
#include <malloc.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
|
||||
// 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 };
|
||||
|
||||
inline void* operator new (size_t size, void *place) {
|
||||
/*inline void* operator new (size_t size, void *place) {
|
||||
return place;
|
||||
}
|
||||
}*/
|
||||
|
||||
// Fast and simple B+ tree of simple types
|
||||
// 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>
|
||||
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;
|
||||
// Get parent and adjust the links
|
||||
if (nodeLevel) {
|
||||
@ -612,11 +610,9 @@ bool BePlusTree<Value, Key, Allocator, KeyOfValue, Cmp, LeafCount, NodeCount>::a
|
||||
newNode = lower;
|
||||
curLevel--;
|
||||
}
|
||||
Value temp = (*(ItemList*)newNode)[0];
|
||||
addErrorValue = (*(ItemList*)newNode)[0];
|
||||
((ItemList *)newNode)->~ItemList();
|
||||
pool->free(newNode);
|
||||
addErrorValue = temp; // We can do this only after all deallocations to prevent problems
|
||||
// caused by re-enterance from allocator
|
||||
throw;
|
||||
}
|
||||
return true;
|
||||
|
Loading…
Reference in New Issue
Block a user