/** * \file This holds all stufff related our memory managent. * I try the best as far as I can to reduce memory fragmentation * and unneccessary calls to alloc and free. * * To achive this I try an approach described here as "Quick Fit". * http://www.flounder.com/memory_allocation.htm * * The basic idea is to keep allocated memory segments and don't free * them again. Instead I will put them in a tree indexed by their size. * To get new memory I first have a look in the tree if there is * a fitting memory segment. Fitting mean, larger or exactly the size * I need. If there is one, use it. If not create a new one using * usual malloc approach. * I won't split the reagions at all because most likely they will be * free soon again. This way I might waste some memory, so I have to * keep an eye on this. * * Right now I don't build an upper limit for allocation. The limit * still is the system memory itself. * * This is not implemented as a class because it will be used in the * process of object creation. * * The data structure is a balanced tree with size as key. * Under the size key is a list of elements of the same size. * * \author Georg Hopp * * \copyright * Copyright © 2012 Georg Hopp * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define _GNU_SOURCE #include #include #include #include #include #include "utils/memory.h" #include "tree.h" struct memSegment { size_t size; void * ptr; enum rbColor color; struct memSegment * next; struct memSegment * last; struct memSegment * parent; struct memSegment * left; struct memSegment * right; }; struct memSegment * newElement(size_t size) { long psize = sysconf(_SC_PAGESIZE); /* allocate only blocks of a multiple of pagesize, similar to cbuf */ size = (0 >= size)? 1 : (0 != size%psize)? (size/psize)+1 : size/psize; size *= psize; struct memSegment * element = malloc(size + sizeof(struct memSegment)); element->size = size; element->ptr = (void*)element + sizeof(struct memSegment); element->next = NULL; element->last = NULL; element->color = rbRed; element->parent = NULL; element->left = NULL; element->right = NULL; return element; } /** * find element in tree */ struct memSegment * findElement(struct memSegment * tree, size_t size) { struct memSegment * fitting = NULL; while (NULL != tree) { if (tree->size == size) { fitting = tree; break; } if (size > tree->size) { tree = tree->right; } else { fitting = tree; tree = tree->left; } } return fitting; } /* * function to get specific elements needed for * rb handling, grandparent, uncle and sibbling */ struct memSegment * grandparent(struct memSegment * node) { if (NULL != node && NULL != node->parent) { return node->parent->parent; } return NULL; } struct memSegment * uncle(struct memSegment * node) { struct memSegment * gp = grandparent(node); if (NULL == gp) { return NULL; } if (node->parent == gp->left) { return gp->right; } return gp->left; } struct memSegment * sibling(struct memSegment * node) { if (NULL == node) { return NULL; } if (NULL == node->parent->left || node == node->parent->left) { return node->parent->right; } else { return node->parent->left; } } /* * tree modifications...needed for rb handling. */ void rotateLeft(struct memSegment ** tree, struct memSegment * node) { struct memSegment * rightChild = node->right; struct memSegment * rcLeftSub = node->right->left; rightChild->left = node; rightChild->parent = node->parent; node->right = rcLeftSub; if (NULL != rcLeftSub) { rcLeftSub->parent = node; } if (node->parent) { if (node->parent->left == node) { node->parent->left = rightChild; } else { node->parent->right = rightChild; } } else { *tree = rightChild; } node->parent = rightChild; } void rotateRight(struct memSegment ** tree, struct memSegment * node) { struct memSegment * leftChild = node->left; struct memSegment * lcRightSub = node->left->right; leftChild->right = node; leftChild->parent = node->parent; node->left = lcRightSub; if (NULL != lcRightSub) { lcRightSub->parent = node; } if (node->parent) { if (node->parent->left == node) { node->parent->left = leftChild; } else { node->parent->right = leftChild; } } else { *tree = leftChild; } node->parent = leftChild; } void replaceNode( struct memSegment ** tree, struct memSegment * node1, struct memSegment * node2) { if (NULL != node1->parent) { if (node1 == node1->parent->left) { node1->parent->left = node2; } else { node1->parent->right = node2; } } else { *tree = node2; } if (NULL != node2) { node2->parent = node1->parent; } } /** * insert element in tree */ struct memSegment * insertElement(struct memSegment ** tree, struct memSegment * element) { struct memSegment * node = *tree; struct memSegment * new_node = NULL; struct memSegment * u; struct memSegment * g; element->next = NULL; element->last = NULL; element->color = rbRed; element->parent = NULL; element->left = NULL; element->right = NULL; // if tree is empty it's simple... :) if (NULL == node) { *tree = node = new_node = element; } else { // normal binary tree add.... while (NULL != node) { if (element->size < node->size) { if (NULL == node->left) { node->left = element; node->left->parent = node; new_node = node = node->left; break; } else { node = node->left; } } else if (element->size > node->size) { if (NULL == node->right) { node->right = element; node->right->parent = node; new_node = node = node->right; break; } else { node = node->right; } } else { if (NULL == node->next) { node->next = element; node->last = element; } else { node->last->next = element; node->last = element; } return node; } } } if (NULL != new_node) { /* * handle reballancing rb style */ while (1) { // case 1 if (node->parent == NULL) { node->color = rbBlack; // we're done.... :) break; } // case 2 if (node->parent->color == rbBlack) { // Tree is still valid ... wow, again we're done... :) break; } // case 3 u = uncle(node); g = grandparent(node); if (u != NULL && u->color == rbRed) { node->parent->color = rbBlack; u->color = rbBlack; g->color = rbRed; node = g; continue; } // case 4 if (node == node->parent->right && node->parent == g->left) { rotateLeft(tree, node->parent); node = node->left; } else if (node == node->parent->left && node->parent == g->right) { rotateRight(tree, node->parent); node = node->right; } // case 5 g = grandparent(node); node->parent->color = rbBlack; g->color = rbRed; if (node == node->parent->left) { rotateRight(tree, g); } else { rotateLeft(tree, g); } // we're done.. break; } } return new_node; } /** * delete element from tree * here multiple functions are involved.... * ======================================================================= */ /** * find minimum of the right subtree aka leftmost leaf of right subtree * aka left in-order successor. * We return the parent of the element in the out argument parent. * This can be NULL wenn calling. * * 2: *successor = {size = 80, ptr = 0x603ae0, color = rbRed, parent = 0x603160, * left = 0x0, right = 0x0} * 1: *node = {size = 70, ptr = 0x603a60, color = rbBlack, parent = 0x603070, * left = 0x6030e0, right = 0x6031e0} * */ struct memSegment * findInOrderSuccessor(struct memSegment * tree) { struct memSegment * node = tree->right; while (NULL != node->left) { node = node->left; } return node; } struct memSegment * deleteElement(struct memSegment ** tree, struct memSegment * element) { struct memSegment * node = *tree; struct memSegment * del_node; struct memSegment * child; struct memSegment * s; // find the relevant node and it's parent while (NULL != node) { if (element->size < node->size) { node = node->left; } else if (element->size > node->size) { node = node->right; } else { if (NULL != node->next) { if (NULL != node->parent) { if (node == node->parent->left) { node->parent->left = node->next; } else { node->parent->right = node->next; } } else { *tree = node->next; } if (NULL != node->left) { node->left->parent = node->next; } if (NULL != node->right) { node->right->parent = node->next; } node->next->last = node->last; node->next->color = node->color; node->next->parent = node->parent; node->next->left = node->left; node->next->right = node->right; return node; } break; } } // element not found if (NULL == node) { return node; } del_node = node; // now our cases follows...the first one is the same as with // simple binary search trees. Two non null children. // case 1: two children if (NULL != node->left && NULL != node->right) { struct memSegment * successor = findInOrderSuccessor(node); enum rbColor tmpcolor = successor->color; struct memSegment * tmpparent = successor->parent; struct memSegment * tmpleft = successor->left; struct memSegment * tmpright = successor->right; replaceNode(tree, node, successor); successor->color = node->color; successor->left = node->left; successor->left->parent = successor; // the right one might be successor... if (node->right == successor) { successor->right = node; node->parent = successor; } else { successor->right = node->right; node->right->parent = successor; node->parent = tmpparent; tmpparent->left = node; } node->color = tmpcolor; node->left = tmpleft; node->right = tmpright; } // Precondition: n has at most one non-null child. child = (NULL == node->right) ? node->left : node->right; replaceNode(tree, node, child); // delete one child case // TODO this is overly complex as simply derived from the function... // maybe this can be simplified. Maybe not...check. if (node->color == rbBlack) { if (NULL != child && child->color == rbRed) { child->color = rbBlack; // done despite modifying tree itself if neccessary.. return del_node; } else { if (NULL != child) { node = child; } else { node->color = rbBlack; node->left = NULL; node->right = NULL; } } } else { return del_node; } // delete and rb rebalance... while(1) { // case 1 if (NULL == node->parent) { // done again break; } // case 2 s = sibling(node); if (NULL != s && s->color == rbRed) { node->parent->color = rbRed; s->color = rbBlack; /* * detect which child we are...assumption * if we are not parent->right and parent->right is not * null we must be left, even if its set to NULL previously */ if (NULL != node->parent->right && node != node->parent->right) { rotateLeft(tree, node->parent); } else { rotateRight(tree, node->parent); } } s = sibling(node); // case 3 / 4 if (NULL == s || ((s->color == rbBlack) && (NULL == s->left || s->left->color == rbBlack) && (NULL == s->right || s->right->color == rbBlack))) { if (NULL != s) { s->color = rbRed; } if (node->parent->color == rbBlack) { // case 3 node = node->parent; continue; } else { // case 4 node->parent->color = rbBlack; // and done again... break; } } // case 5 if (NULL != s && s->color == rbBlack) { // this if statement is trivial, // due to case 2 (even though case 2 changed the sibling to a // sibling's child, // the sibling's child can't be red, since no red parent can // have a red child). // // the following statements just force the red to be on the // left of the left of the parent, // or right of the right, so case 6 will rotate correctly. if ((node == node->parent->left) && (NULL == s->right || s->right->color == rbBlack) && (NULL != s->left && s->left->color == rbRed)) { // this last test is trivial too due to cases 2-4. s->color = rbRed; s->left->color = rbBlack; rotateRight(tree, s); } else if ((node == node->parent->right) && (NULL == s->left || s->left->color == rbBlack) && (NULL != s->right && s->right->color == rbRed)) { // this last test is trivial too due to cases 2-4. s->color = rbRed; s->right->color = rbBlack; rotateLeft(tree, s); } } s = sibling(node); // case 6 if (NULL != s) { s->color = node->parent->color; } if (NULL != node && NULL != node->parent) { node->parent->color = rbBlack; /* * detect which child we are...assumption * if we are not parent->right and parent->right is not * null we must be left, even if its set to NULL previously */ if (NULL != node->parent->right && node != node->parent->right) { if (NULL != s->right) { s->right->color = rbBlack; } rotateLeft(tree, node->parent); } else { if (NULL != s->left) { s->left->color = rbBlack; } rotateRight(tree, node->parent); } } // done... break; } return del_node; } void traverse(struct memSegment * tree, void (*cb)(struct memSegment *, int)) { struct memSegment * previous = tree; struct memSegment * node = tree; int depth = 1; /* * I think this has something like O(n+log(n)) on a ballanced * tree because I have to traverse back the rightmost leaf to * the root to get a break condition. */ while (node) { /* * If we come from the right so nothing and go to our * next parent. */ if (previous == node->right) { previous = node; node = node->parent; depth--; continue; } if ((NULL == node->left || previous == node->left)) { /* * If there are no more elements to the left or we * came from the left, process data. */ cb(node, depth); previous = node; if (NULL != node->right) { node = node->right; depth++; } else { node = node->parent; depth--; } } else { /* * if there are more elements to the left go there. */ previous = node; node = node->left; depth++; } } } void post(struct memSegment * tree, void (*cb)(struct memSegment *, int)) { struct memSegment * previous = tree; struct memSegment * node = tree; int depth = 1; /* * I think this has something like O(n+log(n)) on a ballanced * tree because I have to traverse back the rightmost leaf to * the root to get a break condition. */ while (node) { /* * If we come from the right so nothing and go to our * next parent. */ if (((NULL == node->left || previous == node->left) && NULL == node->right) || previous == node->right) { struct memSegment * parent = node->parent; cb(node, depth); previous = node; node = parent; depth--; continue; } if ((NULL == node->left || previous == node->left)) { /* * If there are no more elements to the left or we * came from the left, process data. */ previous = node; if (NULL != node->right) { node = node->right; depth++; } else { node = node->parent; depth--; } } else { /* * if there are more elements to the left go there. */ previous = node; node = node->left; depth++; } } } void printElement(struct memSegment * node, int depth) { int i; printf("%s %010zu:%p(%02d)", (node->color==rbRed)?"R":"B", node->size, node->ptr, depth); for (i=0; inext; while (NULL != node) { printf(" %s %010zu:%p(%02d)", (node->color==rbRed)?"R":"B", node->size, node->ptr, depth); for (i=0; inext; } } void cleanup(struct memSegment * node, int depth) { while (NULL != node) { struct memSegment * next = node->next; free(node); node = next; } } struct memSegment * segments = NULL; // /** // * this will interpret any memory segment that is not smaller // * than the expected size as fitting. // * // * @param void * size_ptr a pointer to a size_t value searched for // * @param void * subject a pointer to the currently analysed tree element // */ // static // int // segmentFindCmp(const void * size_ptr, const void * subject) // { // if (*(size_t *)size_ptr > ((struct memSegment *)subject)->size) // return 1; // // return 0; // } // // /** // * this returns exact fits....uhh.....I can't relate solely on // * the size argument as then same sized segments will never // * be stored. // * Maybe a tree is not the best data structure to use to store // * these. // * Anyway, right now take the ptr into account if size if equal. // */ // static // int // segmentSearchCmp(const void * search, const void * subject) // { // size_t idx = // ((struct memSegment *)search)->size - // ((struct memSegment *)subject)->size; // // if (0 == idx) { // return // ((struct memSegment *)search)->ptr - // ((struct memSegment *)subject)->ptr; // } // // return idx; // } static void segmentFree(struct memSegment * segment, int depth) { while (NULL != segment) { struct memSegment * next = segment->next; free(segment); segment = next; } } /* * This will always allocate a multiple of PAGESIZE */ void * memMalloc(size_t size) { struct memSegment * seg; long psize = sysconf(_SC_PAGESIZE); size = (0 >= size)? 1 : (0 != size%psize)? (size/psize)+1 : size/psize; size *= psize; seg = findElement(segments, size); if (NULL == seg) { seg = newElement(size); } else { // remove the found one from the tree as we use it now. seg = deleteElement(&segments, seg); } return seg->ptr; } /** * this is a really memory wasting solution....just to be able to * use calloc, which might be faster then malloc/memset solution. * * Maybe this is a bad idea, as we need to memset the buffer anyway * if it comes from our tree, which hopefully should be the majority * of cases. */ void * memCalloc(size_t nmemb, size_t size) { size_t _size = nmemb * size; void * mem = memMalloc(_size); memset(mem, 0, _size); return mem; } void memFree(void ** mem) { if (NULL != *mem) { insertElement(&segments, (struct memSegment *)(*mem - sizeof(struct memSegment))); *mem = NULL; } } size_t memGetSize(void * mem) { struct memSegment * segment; if (NULL == mem) { return 0; } segment = (struct memSegment *)(mem - sizeof(struct memSegment)); return segment->size; } void memCleanup() { post(segments, segmentFree); } void ffree(void ** data) { if (NULL != *data) { free(*data); *data = NULL; } } // vim: set ts=4 sw=4: