Changeset 631


Ignore:
Timestamp:
11/27/05 15:29:26 (6 years ago)
Author:
wd
Message:

Compiled, SHIP IT version of memory allocator with down-tuning. We need
more work and rigorous code-test. :)

Location:
trunk/ithildin
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/ithildin/include/ithildin/malloc.h

    r578 r631  
    2727#endif 
    2828 
     29extern struct _malloc_statistics { 
     30    uint64_t    malloc_calls;       /* Number of calls to malloc/calloc */ 
     31    uint64_t    realloc_calls;      /* Number of callcs to realloc */ 
     32    uint64_t    free_calls;         /* Number of calls to free() */ 
     33 
     34    uint64_t    heaps;              /* Number of 'performance managed' heaps */ 
     35 
     36    uint64_t    palloc_calls;       /* Number of calls to page allocator */ 
     37    uint64_t    pfree_calls;        /* Number of calls to page freer */ 
     38     
     39    size_t      pagesize;           /* Size (in bytes) of allocated blocks */ 
     40    uint64_t    pages_inuse;        /* Current count of blocks in-use */ 
     41} malloc_statistics; 
     42 
     43/* Use the MALLOC_STATISTICS macro to easily test whether or not allocator 
     44 * statistics are available for consumption.  The thing it is using to 
     45 * determine this may change over time. */ 
     46#define MALLOC_STATISTICS malloc_statistics.pagesize 
     47 
    2948#endif 
    3049 
  • trunk/ithildin/source/malloc.c

    r589 r631  
    22 * malloc.c: optimised memory allocation routines 
    33 *  
    4  * Copyright 2003 the Ithildin Project. 
     4 * Copyright 2003-2005 the Ithildin Project. 
    55 * See the COPYING file for more information on licensing and use. 
    66 *  
     
    99 * decisions about how to allocate chunks of memory.  it is optimised for the 
    1010 * "thousands or more allocations of same-sized memory" case. 
     11 * 
     12 * In brief:  As allocations of same-sized memory continue, the allocator 
     13 * begins asking for more hunks of memory at once to fill requests for that 
     14 * "same size."  It caps out at a certain point.  Additionally, at periodic 
     15 * intervals, a check is performed to determine how well things are going 
     16 * and to "reap" memory that has been pre-allocated and is not (apparently) 
     17 * in use.  A sliding scale is used to determine how aggressively we should 
     18 * turn back the dial on fresh allocations. 
    1119 */ 
    1220 
    1321#include <ithildin/stand.h> 
     22 
     23/* Keep the statistics structure here so it is always available, macros in 
     24 * malloc.h will provide a simple test for its usability. */ 
     25 
     26struct _malloc_statistics malloc_statistics; 
     27 
    1428#ifdef USE_INTERNAL_MALLOC 
    1529 
     
    3246 * than this will always be one page or more because at this size it becomes 
    3347 * inefficient to try and wrangle these allocations into being any less 
    34  * wasteful than that */ 
    35 #define MALLOC_MAX_SIZE (MALLOC_PAGESIZE / 2) 
     48 * wasteful than that.  That is, at a size larger than this we are unable to 
     49 * keep more than one allocation and a block structure within a single page, 
     50 * so there is a lot of waste.  */ 
     51#define MALLOC_MAX_SIZE ((MALLOC_PAGESIZE - sizeof(struct block)) / 2) 
    3652/* number of blocks to 'cache' in the system.  if more blocks than this are 
    3753 * free the library will return the pages to the system. */ 
    3854#define MALLOC_CACHE_BLOCKS 1 
     55/* number of calls (total) to malloc/calloc/realloc/free before we "retune." 
     56 * Note that retuning is only done if we are optimizing. */ 
     57#define MALLOC_RETUNE_AFTER 65536 
     58 
    3959/* location of the configuration file we use. */ 
    4060#define MALLOC_CONFIG_FILE "/etc/malloc.conf" 
     
    4666 * the options in his allocator map to the options in this allocator. */ 
    4767static struct { 
    48     bool    wfatal;                /* warnings become fatal */ 
    49     bool    fill;                /* fill memory with 'fbyte' when allocated and 
     68    bool    wfatal;             /* warnings become fatal */ 
     69    bool    fill;               /* fill memory with 'fbyte' when allocated and 
    5070                                   freed. */ 
    5171    char    fbyte; 
    52     bool    force_realloc;        /* force a memory re-allocation even if the 
     72    bool    force_realloc;      /* force a memory re-allocation even if the 
    5373                                   memory's current area is large enough to 
    5474                                   hold the new data without one */ 
    5575    bool    sysv_compat;        /* 0-byte allocation returns NULL? */ 
    5676    bool    nomem_fatal;        /* abort() if no memory is available. */ 
    57     int            page_cache;                /* number of pages to cache at the end of 
     77    unsigned int page_cache;    /* number of pages to cache at the end of 
    5878                                   memory before returning memory to the 
    5979                                   system. */ 
    60     int            block_cache;        /* number of empty blocks to cache for each 
     80    unsigned int block_cache;   /* number of empty blocks to cache for each 
    6181                                   heap. <1 forces immediate return of these 
    6282                                   blocks to the system. */ 
    63     bool    sanity;                /* do extra sanity checking to better handle 
     83    bool    sanity;             /* do extra sanity checking to better handle 
    6484                                   corrupted memory */ 
    6585    bool    optimize;           /* optimize with "look-ahead" allocations */ 
     86    unsigned int retune;        /* Number of calls between retuning */ 
    6687} options; 
    6788 
     
    7192 
    7293/***************************************************************************** 
    73  * low-level memory management code                                             * 
     94 * low-level memory management code                                          * 
    7495 *****************************************************************************/ 
    7596/* this is the low-level malloc system code.  this code is used to track pages 
     
    87108 * mmap.  we also store the offset of the first free page in the count so that 
    88109 * we can find free pages somewhat more quickly. */ 
    89 #define MALLOC_PAGE_OTHER        0 
     110#define MALLOC_PAGE_OTHER       0 
    90111#define MALLOC_PAGE_FREE        0x01 
    91 #define MALLOC_PAGE_BALLOC        0x02 
    92 #define MALLOC_PAGE_ALLOC        0x04 
    93 #define MALLOC_PAGE_START        (MALLOC_PAGE_BALLOC | MALLOC_PAGE_ALLOC) 
    94 #define MALLOC_PAGE_CONTINUED        0x08 
     112#define MALLOC_PAGE_BALLOC      0x02 
     113#define MALLOC_PAGE_ALLOC       0x04 
     114#define MALLOC_PAGE_START       (MALLOC_PAGE_BALLOC | MALLOC_PAGE_ALLOC) 
     115#define MALLOC_PAGE_CONTINUED   0x08 
    95116static struct { 
    96117    char *start;                        /* start of memory */ 
     
    153174    memset(s, MALLOC_PAGE_FREE, count); 
    154175 
     176    malloc_statistics.pages_inuse += count; 
     177 
    155178    return true; 
    156179} 
     
    181204            while (--count != 0) 
    182205                page_desc.parray[idx + count] = MALLOC_PAGE_CONTINUED; 
     206 
     207            /* Increment here because we may call ourself recursively */ 
     208            malloc_statistics.palloc_calls++; 
    183209            return page_desc.start + (MALLOC_PAGESIZE * idx); 
    184210        } 
     
    198224static void free_pages(void *mem) { 
    199225    unsigned int idx, eidx; 
     226 
     227    malloc_statistics.pfree_calls++; 
    200228 
    201229    if ((long)mem % MALLOC_PAGESIZE != 0) { 
     
    211239        eidx++; 
    212240    memset(page_desc.parray + idx, MALLOC_PAGE_FREE, eidx - idx); 
     241    malloc_statistics.pages_inuse -= (eidx - idx); 
    213242 
    214243    /* see if there are pages at the end of the cache that can be given back to 
     
    232261 
    233262/***************************************************************************** 
    234  * actual malloc code                                                             * 
     263 * actual malloc code                                                        * 
    235264 *****************************************************************************/ 
    236265/* the heap structure.  these are the top-level memory sources.  there is one 
    237266 * for each allocation size available.  using the defaults from above this 
    238267 * yields 47 separate heaps. */ 
    239 #define MALLOC_HEAP_COUNT                                                \ 
    240 (((MALLOC_MAX_SIZE - MALLOC_MIN_SIZE) / MALLOC_FUZZ) + 1) 
    241268struct heap { 
    242269    size_t size;                    /* size allocated to the heap */ 
    243     int etotal;                     /* total element count */ 
    244     int ealloc;                     /* allocated element count */ 
    245  
    246     int blocks;                     /* 'blocks' of memory allocated */ 
    247     int bpages;                     /* number of pages we're using for blocks, will 
     270    uint64_t etotal;                /* total element count */ 
     271    uint64_t ealloc;                /* allocated element count */ 
     272 
     273    uint32_t blocks;                /* 'blocks' of memory allocated */ 
     274    uint32_t bpages;                /* number of pages we're using for blocks, will 
    248275                                       increase until bpages > MALLOC_MAX_PAGES or 
    249276                                       the number of elements in a block would be > 
    250277                                       MALLOC_MAX_ELEMS */ 
    251     int bempty;                     /* blocks which are empty */ 
    252     int bfull;                      /* blockx which are full */ 
    253  
    254     int allocs;                     /* 'alloc' calls */ 
    255     int frees;                      /* 'free' calls */ 
     278    uint32_t bempty;                /* blocks which are empty */ 
     279    uint32_t bfull;                 /* blockx which are full */ 
     280 
     281    uint64_t allocs;                /* 'alloc' calls */ 
     282    uint64_t frees;                 /* 'free' calls */ 
    256283 
    257284    LIST_HEAD(, block) blist;       /* list of allocated blocks */ 
    258285    LIST_HEAD(, block) oblist;      /* list of blocks with some free memory */ 
    259 } heaps[MALLOC_HEAP_COUNT]; 
     286}; 
    260287 
    261288/* a block is a region of contiguous memory.  the size of a block may vary from 
     
    286313}; 
    287314 
     315/* 
     316 * We keep a static grouping of the heaps structures here.  We declare this 
     317 * last in order to understand the sizes of things (note the use of 
     318 * MALLOC_MAX_SIZE). 
     319 */ 
     320#define MALLOC_HEAP_COUNT                                                      \ 
     321    (((MALLOC_MAX_SIZE - MALLOC_MIN_SIZE) / MALLOC_FUZZ) + 1) 
     322struct heap heaps[MALLOC_HEAP_COUNT]; 
     323 
    288324/***************************************************************************** 
    289  * initialization code                                                             * 
     325 * initialization code                                                       * 
    290326 *****************************************************************************/ 
    291327const char *_malloc_options = NULL; 
     
    296332    while (*buf != '\0') { 
    297333        switch (*buf) { 
    298         case '>': options.page_cache <<= 1;                        break; 
    299         case '<': options.page_cache >>= 1;                        break; 
    300         case 'a': options.wfatal = false;                        break; 
    301         case 'A': options.wfatal = true;                        break; 
    302         case 'b': options.block_cache--;                        break; 
    303         case 'B': options.block_cache++;                        break; 
    304         case 'j': options.fill = false;                                break; 
     334        case '>': options.page_cache <<= 1;                         break; 
     335        case '<': options.page_cache >>= 1;                         break; 
     336        case 'a': options.wfatal = false;                           break; 
     337        case 'A': options.wfatal = true;                            break; 
     338        case 'b': options.block_cache--;                            break; 
     339        case 'B': options.block_cache++;                            break; 
     340        case 'j': options.fill = false;                             break; 
    305341        case 'J': options.fill = true; options.fbyte = 0xd0;        break; 
    306         case 'l': options.optimize = false;                     break; 
    307         case 'L': options.optimize = true;                      break; 
    308         case 'r': options.force_realloc = false;                break; 
    309         case 'R': options.force_realloc = true;                        break; 
    310         case 's': options.sanity = false;                        break; 
    311         case 'S': options.sanity = true;                        break; 
    312         case 'v': options.sysv_compat = false;                        break; 
    313         case 'V': options.sysv_compat = true;                        break; 
    314         case 'z': options.fill = false;                                break; 
    315         case 'Z': options.fill = true; options.fbyte = 0x0;        break; 
     342        case 'l': options.optimize = false;                         break; 
     343        case 'L': options.optimize = true;                          break; 
     344        case 'r': options.force_realloc = false;                    break; 
     345        case 'R': options.force_realloc = true;                     break; 
     346        case 's': options.sanity = false;                           break; 
     347        case 'S': options.sanity = true;                            break; 
     348        case 't': options.retune /= 2;                              break; 
     349        case 'T': options.retune *= 2;                              break; 
     350        case 'v': options.sysv_compat = false;                      break; 
     351        case 'V': options.sysv_compat = true;                       break; 
     352        case 'z': options.fill = false;                             break; 
     353        case 'Z': options.fill = true; options.fbyte = 0x0;         break; 
    316354        } 
    317355        buf++; 
     
    362400static bool malloc_initialized = false; 
    363401static void malloc_init(void) { 
    364     int i; 
     402    unsigned int i; 
    365403    size_t sz; 
    366404#define BUFSIZE 64 
     
    370408    options.page_cache = MALLOC_PAGE_CACHE; 
    371409    options.block_cache = MALLOC_CACHE_BLOCKS; 
     410    options.retune = MALLOC_RETUNE_AFTER; 
    372411    options.optimize = true; 
    373412 
     
    396435        heaps[i].bpages = 1; 
    397436    } 
     437 
     438    malloc_statistics.heaps = MALLOC_HEAP_COUNT; 
     439    malloc_statistics.pagesize = MALLOC_PAGESIZE; 
     440     
    398441    malloc_initialized = true; 
    399442} 
     
    432475static struct block *create_block(struct heap *hp) { 
    433476    struct block *bp; 
    434     struct block_elem *ep; 
     477    struct block_elem *ep, *ep2; 
    435478    int elems; 
    436479    char *mem; 
     
    451494 
    452495    SLIST_INIT(&bp->elist); 
     496    /* Create the list in the order of memory.  We hope to use retuning code 
     497     * later to see if we can reclaim chunks of a block that are not in use. */ 
     498    ep2 = NULL; 
    453499    while (mem < bp->end) { 
    454500        ep = (struct block_elem *)mem; 
    455501        ep->magic = MALLOC_MAGIC; 
    456         SLIST_INSERT_HEAD(&bp->elist, ep, lp); 
     502        if (ep2 == NULL) 
     503            SLIST_INSERT_HEAD(&bp->elist, ep, lp); 
     504        else 
     505            SLIST_INSERT_AFTER(ep2, ep, lp); 
     506 
     507        ep2 = ep; 
    457508 
    458509        mem += hp->size; 
     
    521572        bp->heap->bfull++; 
    522573    } 
     574    bp->heap->ealloc++; 
    523575    ep->magic = 0; /* wipe the magic number */ 
    524576    return ep; 
     
    540592        memset(mem, options.fbyte, bp->heap->size); 
    541593 
     594    bp->heap->ealloc--; 
    542595    ep->magic = MALLOC_MAGIC; 
    543596    SLIST_INSERT_HEAD(&bp->elist, ep, lp); 
     
    592645} 
    593646 
     647/* Retuning function.  Walk the heap list, looking for heaps that are in 
     648 * need of "down-tuning" and do so. */ 
     649#define HEAP_PCT_FREE_REDUCE 0.10 
     650static void retune_blocks(void) { 
     651    unsigned int i; 
     652    struct block *bp, *bp2; 
     653 
     654    for (i = 0;i < MALLOC_HEAP_COUNT;i++) { 
     655        /* Pretty simple, if the heap is HEAP_PCT_FREE_REDUCE % full or 
     656         * less, we reduce its future allocations unless they are already at 
     657         * the lowest possible (1).  We also go ahead and free up any 
     658         * completely empty blocks in the heap. XXX: We should also try and 
     659         * reduce the size of mostly underused blocks when possible. */ 
     660        if ((float)(heaps[i].etotal - heaps[i].ealloc) >= 
     661            (float)heaps[i].etotal * HEAP_PCT_FREE_REDUCE) { 
     662            if (heaps[i].bpages > 1) 
     663                heaps[i].bpages /= 2; /* Reduce! */ 
     664            if (heaps[i].bempty) { 
     665                /* Walk the list and return any empty blocks */ 
     666                bp = LIST_FIRST(&heaps[i].oblist); 
     667 
     668                while (bp != NULL) { 
     669                    bp2 = LIST_NEXT(bp, lp); 
     670 
     671                    if (bp->used == 0) 
     672                        destroy_block(bp); 
     673                } 
     674            } 
     675        } 
     676    } 
     677} 
     678 
    594679/* these are the four allocator functions.  in general they pass off work to 
    595680 * subsystem functions declared below. */ 
     681 
     682static unsigned int call_counter; 
     683#define CHECK_RETUNE(x) do {                                                   \ 
     684    (x)++;                                                                     \ 
     685    if ((x) > options.retune) {                                                \ 
     686        (x) = 0;                                                               \ 
     687        retune_blocks();                                                       \ 
     688    }                                                                          \ 
     689} while (0) 
     690 
     691 
    596692void *calloc(size_t count, size_t size) { 
    597693    void *mem; 
     
    606702    size_t size; 
    607703 
     704    malloc_statistics.free_calls++; 
     705     
    608706    if (recursing) { 
    609707        malloc_warn("free(%p): recursing in allocator", mem); 
     
    625723     
    626724    if (bp == NULL) { 
    627     if (options.fill) 
    628         memset(mem, options.fbyte, size); 
     725        if (options.fill) 
     726            memset(mem, options.fbyte, size); 
    629727        free_pages(mem); 
    630     } else 
     728    } else { 
    631729        free_block(bp, mem); 
     730        CHECK_RETUNE(call_counter); 
     731    } 
    632732 
    633733    recursing--; 
     
    637737    struct block *bp; 
    638738    void *mem; 
     739 
     740    malloc_statistics.malloc_calls++; 
    639741 
    640742    if (recursing) { 
     
    689791 
    690792    recursing--; 
     793    CHECK_RETUNE(call_counter); 
    691794    return mem; /* huzzah! */ 
    692795} 
     
    700803    void *newmem; 
    701804     
     805    malloc_statistics.realloc_calls++; 
     806 
    702807    if (recursing) { 
    703808        malloc_warn("realloc(%p, %d): recursing in allocator", mem, newsize); 
     
    718823        if (newsize == 0) 
    719824            return NULL; 
     825        malloc_statistics.malloc_calls--; /* this will be an artificially 
     826                                             generated call */ 
    720827        return malloc(newsize); 
    721828    } 
     
    730837    /* unset recursing so we can manually call malloc.. */ 
    731838    recursing--; 
    732     if (!options.force_realloc && size >= newsize) 
     839    if (!options.force_realloc && size >= newsize) { 
     840        CHECK_RETUNE(call_counter); 
    733841        return mem; /* no need to move any memory ... */ 
    734  
     842    } 
     843 
     844    malloc_statistics.malloc_calls--; /* this will be an artificially 
     845                                         generated call */ 
    735846    if ((newmem = malloc(newsize)) == NULL) 
    736847        return NULL; /* oops.. */ 
Note: See TracChangeset for help on using the changeset viewer.