Leptonica 1.68
C Image Processing Library

pixalloc.c

Go to the documentation of this file.
00001 /*====================================================================*
00002  -  Copyright (C) 2001 Leptonica.  All rights reserved.
00003  -  This software is distributed in the hope that it will be
00004  -  useful, but with NO WARRANTY OF ANY KIND.
00005  -  No author or distributor accepts responsibility to anyone for the
00006  -  consequences of using this software, or for whether it serves any
00007  -  particular purpose or works at all, unless he or she says so in
00008  -  writing.  Everyone is granted permission to copy, modify and
00009  -  redistribute this source code, for commercial or non-commercial
00010  -  purposes, with the following restrictions: (1) the origin of this
00011  -  source code must not be misrepresented; (2) modified versions must
00012  -  be plainly marked as such; and (3) this notice may not be removed
00013  -  or altered from any source or modified source distribution.
00014  *====================================================================*/
00015 
00016 /*
00017  *  pixalloc.c
00018  *
00019  *      Custom memory storage with allocator and deallocator
00020  *
00021  *          l_int32       pmsCreate()
00022  *          void          pmsDestroy()
00023  *          void         *pmsCustomAlloc()
00024  *          void          pmsCustomDealloc()
00025  *          void         *pmsGetAlloc()
00026  *          l_int32       pmsGetLevelForAlloc()
00027  *          l_int32       pmsGetLevelForDealloc()
00028  *          void          pmsLogInfo()
00029  */
00030 
00031 #include "allheaders.h"
00032 
00033 /*-------------------------------------------------------------------------*
00034  *                          Pix Memory Storage                             *
00035  *                                                                         *
00036  *  This is a simple utility for handling pix memory storage.  It is       *
00037  *  enabled by setting the PixMemoryManager allocators to the functions    *
00038  *  that are defined here                                                  *
00039  *        pmsCustomAlloc()                                                 *
00040  *        pmsCustomDealloc()                                               *
00041  *  Use pmsCreate() at the beginning to do the pre-allocation, and         *
00042  *  pmsDestroy() at the end to clean it up.                                *
00043  *-------------------------------------------------------------------------*/
00044 /*
00045  *  In the following, the "memory" refers to the image data
00046  *  field that is used within the pix.  The memory store is a
00047  *  continuous block of memory, that is logically divided into
00048  *  smaller "chunks" starting with a set at a minimum size, and
00049  *  followed by sets of increasing size that are a power of 2 larger
00050  *  than the minimum size.  You must specify the number of chunks
00051  *  of each size.
00052  *
00053  *  A requested data chunk, if it exists, is borrowed from the memory
00054  *  storage, and returned after use.  If the chunk is too small, or
00055  *  too large, or if all chunks in the appropriate size range are
00056  *  in use, the memory is allocated dynamically and freed after use.
00057  *
00058  *  There are four parameters that determine the use of pre-allocated memory:
00059  *
00060  *    minsize: any requested chunk smaller than this is allocated
00061  *             dynamically and destroyed after use.  No preallocated
00062  *             memory is used.
00063  *    smallest: the size of the smallest pre-allocated memory chunk.
00064  *    nlevels:  the number of different sizes of data chunks, each a
00065  *              power of 2 larger than 'smallest'.
00066  *    numalloc: a Numa of size 'nlevels' containing the number of data
00067  *              chunks for each size that are in the memory store.
00068  *
00069  *  As an example, suppose:
00070  *    minsize = 0.5MB
00071  *    smallest = 1.0MB
00072  *    nlevels = 4
00073  *    numalloc = {10, 5, 5, 5}
00074  *  Then the total amount of allocated memory (in MB) is
00075  *    10 * 1 + 5 * 2 + 5 * 4 + 5 * 8 = 80 MB
00076  *  Any pix requiring less than 0.5 MB or more than 8 MB of memory will
00077  *  not come from the memory store.  Instead, it will be dynamically
00078  *  allocated and freed after use.
00079  *             
00080  *  How is this implemented?
00081  *
00082  *  At setup, the full data block size is computed and allocated.
00083  *  The addresses of the individual chunks are found, and the pointers
00084  *  are stored in a set of Ptra (generic pointer arrays), using one Ptra
00085  *  for each of the sizes of the chunks.  When returning a chunk after
00086  *  use, it is necessary to determine from the address which size level
00087  *  (ptra) the chunk belongs to.  This is done by comparing the address
00088  *  of the associated chunk.
00089  *
00090  *  In the event that memory chunks need to be dynamically allocated,
00091  *  either (1) because they are too small or too large for the memory
00092  *  store or (2) because all the pix of that size (i.e., in the
00093  *  appropriate level) in the memory store are in use, the
00094  *  addresses generated will be outside the pre-allocated block.
00095  *  After use they won't be returned to a ptra; instead the deallocator
00096  *  will free them.
00097  */
00098 
00099 
00100 struct PixMemoryStore
00101 {
00102     struct L_Ptraa  *paa;          /* Holds ptrs to allocated memory        */
00103     size_t           minsize;      /* Pix smaller than this (in bytes)      */
00104                                    /* are allocated dynamically             */
00105     size_t           smallest;     /* Smallest mem (in bytes) alloc'd       */
00106     size_t           largest;      /* Larest mem (in bytes) alloc'd         */
00107     size_t           nbytes;       /* Size of allocated block w/ all chunks */
00108     l_int32          nlevels;      /* Num of power-of-2 sizes pre-alloc'd   */
00109     size_t          *sizes;        /* Mem sizes at each power-of-2 level    */
00110     l_int32         *allocarray;   /* Number of mem alloc'd at each size    */
00111     l_uint32        *baseptr;      /* ptr to allocated array                */
00112     l_uint32        *maxptr;       /* ptr just beyond allocated memory      */
00113     l_uint32       **firstptr;     /* array of ptrs to first chunk in size  */
00114     l_int32         *memused;      /* log: total # of pix used (by level)   */
00115     l_int32         *meminuse;     /* log: # of pix in use (by level)       */
00116     l_int32         *memmax;       /* log: max # of pix in use (by level)   */
00117     l_int32         *memempty;     /* log: # of pix alloc'd because         */
00118                                    /*      the store was empty (by level)   */
00119     char            *logfile;      /* log: set to null if no logging        */
00120 };
00121 typedef struct PixMemoryStore   L_PIX_MEM_STORE;
00122 
00123 static L_PIX_MEM_STORE  *CustomPMS = NULL;
00124 
00125 
00126 /*!
00127  *  pmsCreate()
00128  *
00129  *      Input:  minsize (of data chunk that can be supplied by pms)
00130  *              smallest (bytes of the smallest pre-allocated data chunk.
00131  *              numalloc (array with the number of data chunks for each
00132  *                        size that are in the memory store)
00133  *              logfile (use for debugging; null otherwise)
00134  *      Return: 0 if OK, 1 on error
00135  *
00136  *  Notes:
00137  *      (1) This computes the size of the block of memory required
00138  *          and allocates it.  Each chunk starts on a 32-bit word boundary.
00139  *          The chunk sizes are in powers of 2, starting at @smallest,
00140  *          and the number of levels and chunks at each level is
00141  *          specified by @numalloc.
00142  *      (2) This is intended to manage the image data for a small number
00143  *          of relatively large pix.  The system malloc is expected to
00144  *          handle very large numbers of small chunks efficiently.
00145  *      (3) Important: set the allocators and call this function
00146  *          before any pix have been allocated.  Destroy all the pix
00147  *          in the normal way before calling pmsDestroy().
00148  *      (4) The pms struct is stored in a static global, so this function
00149  *          is not thread-safe.  When used, there must be only one thread
00150  *          per process.
00151  */
00152 l_int32
00153 pmsCreate(size_t       minsize,
00154           size_t       smallest,
00155           NUMA        *numalloc,
00156           const char  *logfile)
00157 {
00158 l_int32           nlevels, i, j, nbytes;
00159 l_int32          *alloca;
00160 l_float32         nchunks;
00161 l_uint32         *baseptr, *data;
00162 l_uint32        **firstptr;
00163 size_t           *sizes;
00164 L_PIX_MEM_STORE  *pms;
00165 L_PTRA           *pa;
00166 L_PTRAA          *paa;
00167 
00168     PROCNAME("createPMS");
00169 
00170     if (!numalloc)
00171         return ERROR_INT("numalloc not defined", procName, 1);
00172     numaGetSum(numalloc, &nchunks);
00173     if (nchunks > 1000.0)
00174         L_WARNING_FLOAT("There are %.0f chunks", procName, nchunks);
00175 
00176     if ((pms = (L_PIX_MEM_STORE *)CALLOC(1, sizeof(L_PIX_MEM_STORE))) == NULL)
00177         return ERROR_INT("pms not made", procName, 1);
00178     CustomPMS = pms;
00179 
00180         /* Make sure that minsize and smallest are multiples of 32 bit words */
00181     if (minsize % 4 != 0)
00182         minsize -= minsize % 4;
00183     pms->minsize = minsize;
00184     nlevels = numaGetCount(numalloc);
00185     pms->nlevels = nlevels;
00186 
00187     if ((sizes = (size_t *)CALLOC(nlevels, sizeof(size_t))) == NULL)
00188         return ERROR_INT("sizes not made", procName, 1);
00189     pms->sizes = sizes;
00190     if (smallest % 4 != 0)
00191         smallest += 4 - (smallest % 4);
00192     pms->smallest = smallest;
00193     for (i = 0; i < nlevels; i++)
00194         sizes[i] = smallest * (1 << i);
00195     pms->largest = sizes[nlevels - 1];
00196 
00197     alloca = numaGetIArray(numalloc);
00198     pms->allocarray = alloca;
00199     if ((paa = ptraaCreate(nlevels)) == NULL)
00200         return ERROR_INT("paa not made", procName, 1);
00201     pms->paa = paa;
00202 
00203     for (i = 0, nbytes = 0; i < nlevels; i++)
00204         nbytes += alloca[i] * sizes[i];
00205     pms->nbytes = nbytes;
00206 
00207     if ((baseptr = (l_uint32 *)CALLOC(nbytes / 4, sizeof(l_uint32))) == NULL)
00208         return ERROR_INT("calloc fail for baseptr", procName, 1);
00209     pms->baseptr = baseptr;
00210     pms->maxptr = baseptr + nbytes / 4;  /* just beyond the memory store */
00211     if ((firstptr = (l_uint32 **)CALLOC(nlevels, sizeof(l_uint32 *))) == NULL)
00212         return ERROR_INT("calloc fail for firstptr", procName, 1);
00213     pms->firstptr = firstptr;
00214     
00215     data = baseptr;
00216     for (i = 0; i < nlevels; i++) {
00217         if ((pa = ptraCreate(alloca[i])) == NULL)
00218             return ERROR_INT("pa not made", procName, 1);
00219         ptraaInsertPtra(paa, i, pa);
00220         firstptr[i] = data;
00221         for (j = 0; j < alloca[i]; j++) {
00222             ptraAdd(pa, data);
00223             data += sizes[i] / 4;
00224         }
00225     }
00226             
00227     if (logfile) {
00228         pms->memused = (l_int32 *)CALLOC(nlevels, sizeof(l_int32));
00229         pms->meminuse = (l_int32 *)CALLOC(nlevels, sizeof(l_int32));
00230         pms->memmax = (l_int32 *)CALLOC(nlevels, sizeof(l_int32));
00231         pms->memempty = (l_int32 *)CALLOC(nlevels, sizeof(l_int32));
00232         pms->logfile = stringNew(logfile);
00233     }
00234 
00235     return 0;
00236 }
00237 
00238 
00239 /*!
00240  *  pmsDestroy()
00241  *
00242  *      Input:  (none)
00243  *      Return: void
00244  *
00245  *  Notes:
00246  *      (1) Important: call this function at the end of the program, after
00247  *          the last pix has been destroyed.
00248  */
00249 void
00250 pmsDestroy()
00251 {
00252 L_PIX_MEM_STORE  *pms;
00253 
00254     if ((pms = CustomPMS) == NULL)
00255         return;
00256 
00257     ptraaDestroy(&pms->paa, FALSE, FALSE);  /* don't touch the ptrs */
00258     FREE(pms->baseptr);  /* free the memory */
00259 
00260     if (pms->logfile) {
00261         pmsLogInfo();
00262         FREE(pms->logfile);
00263         FREE(pms->memused);
00264         FREE(pms->meminuse);
00265         FREE(pms->memmax);
00266         FREE(pms->memempty);
00267     }
00268 
00269     FREE(pms->sizes);
00270     FREE(pms->allocarray);
00271     FREE(pms->firstptr);
00272     FREE(pms);
00273     CustomPMS = NULL;
00274     return;
00275 }
00276 
00277 
00278 /*!
00279  *  pmsCustomAlloc()
00280  *
00281  *      Input: nbytes (min number of bytes in the chunk to be retrieved)
00282  *      Return: data (ptr to chunk)
00283  *
00284  *  Notes:
00285  *      (1) This attempts to find a suitable pre-allocated chunk.
00286  *          If not found, it dynamically allocates the chunk.
00287  *      (2) If logging is turned on, the allocations that are not taken
00288  *          from the memory store, and are at least as large as the
00289  *          minimum size the store can handle, are logged to file.
00290  */
00291 void *
00292 pmsCustomAlloc(size_t  nbytes)
00293 {
00294 l_int32           level;
00295 void             *data;
00296 L_PIX_MEM_STORE  *pms;
00297 L_PTRA           *pa;
00298 
00299     PROCNAME("pmsCustomAlloc");
00300 
00301     if ((pms = CustomPMS) == NULL)
00302         return (void *)ERROR_PTR("pms not defined", procName, NULL);
00303 
00304     pmsGetLevelForAlloc(nbytes, &level);
00305 
00306     if (level < 0) {  /* size range invalid; must alloc */
00307         if ((data = pmsGetAlloc(nbytes)) == NULL)
00308             return (void *)ERROR_PTR("data not made", procName, NULL);
00309     }
00310     else {  /* get from store */
00311         pa = ptraaGetPtra(pms->paa, level, L_HANDLE_ONLY);
00312         data = ptraRemoveLast(pa);
00313         if (data && pms->logfile) {
00314             pms->memused[level]++; 
00315             pms->meminuse[level]++; 
00316             if (pms->meminuse[level] > pms->memmax[level]) 
00317                 pms->memmax[level]++;
00318         }
00319         if (!data) {  /* none left at this level */
00320             data = pmsGetAlloc(nbytes);
00321             if (pms->logfile)
00322                 pms->memempty[level]++; 
00323         }
00324     }
00325 
00326     return data;
00327 }
00328 
00329 
00330 /*!
00331  *  pmsCustomDealloc()
00332  *
00333  *      Input: data (to be freed or returned to the storage)
00334  *      Return: void
00335  */
00336 void
00337 pmsCustomDealloc(void  *data)
00338 {
00339 l_int32           level;
00340 L_PIX_MEM_STORE  *pms;
00341 L_PTRA           *pa;
00342 
00343     PROCNAME("pmsCustomDealloc");
00344 
00345     if ((pms = CustomPMS) == NULL) {
00346         L_ERROR("pms not defined", procName);
00347         return;
00348     }
00349 
00350     if (pmsGetLevelForDealloc(data, &level) == 1) {
00351         L_ERROR("level not found", procName);
00352         return;
00353     }
00354 
00355     if (level < 0)  /* no logging; just free the data */
00356         FREE(data);
00357     else {  /* return the data to the store */
00358         pa = ptraaGetPtra(pms->paa, level, L_HANDLE_ONLY);
00359         ptraAdd(pa, data);
00360         if (pms->logfile)
00361             pms->meminuse[level]--; 
00362     }
00363 
00364     return;
00365 }
00366 
00367 
00368 /*!
00369  *  pmsGetAlloc()
00370  *
00371  *      Input:  nbytes
00372  *      Return: data
00373  *
00374  *  Notes:
00375  *      (1) This is called when a request for pix data cannot be
00376  *          obtained from the preallocated memory store.  After use it
00377  *          is freed like normal memory.
00378  *      (2) If logging is on, only write out allocs that are as large as
00379  *          the minimum size handled by the memory store.
00380  */
00381 void *
00382 pmsGetAlloc(size_t  nbytes)
00383 {
00384 void             *data;
00385 FILE             *fp;
00386 L_PIX_MEM_STORE  *pms;
00387 
00388     PROCNAME("pmsGetAlloc");
00389 
00390     if ((pms = CustomPMS) == NULL)
00391         return (void *)ERROR_PTR("pms not defined", procName, NULL);
00392 
00393     if ((data = (void *)CALLOC(nbytes, sizeof(char))) == NULL)
00394         return (void *)ERROR_PTR("data not made", procName, NULL);
00395     if (pms->logfile && nbytes >= pms->smallest) {
00396         fp = fopenWriteStream(pms->logfile, "a");
00397         fprintf(fp, "Alloc %ld bytes at %p\n", nbytes, data);
00398         fclose(fp);
00399     }
00400 
00401     return data;
00402 }
00403 
00404 
00405 /*!
00406  *  pmsGetLevelForAlloc()
00407  *
00408  *      Input: nbytes (min number of bytes in the chunk to be retrieved)
00409  *             &level (<return>; -1 if either too small or too large)
00410  *      Return: 0 if OK, 1 on error
00411  */
00412 l_int32
00413 pmsGetLevelForAlloc(size_t    nbytes,
00414                     l_int32  *plevel)
00415 {
00416 l_int32           i;
00417 l_float64         ratio;
00418 L_PIX_MEM_STORE  *pms;
00419 
00420     PROCNAME("pmsGetLevelForAlloc");
00421 
00422     if (!plevel)
00423         return ERROR_INT("&level not defined", procName, 1);
00424     *plevel = -1;
00425     if ((pms = CustomPMS) == NULL)
00426         return ERROR_INT("pms not defined", procName, 1);
00427 
00428     if (nbytes < pms->minsize || nbytes > pms->largest)
00429         return 0;   /*  -1  */
00430 
00431     ratio = (l_float64)nbytes / (l_float64)(pms->smallest);
00432     for (i = 0; i < pms->nlevels; i++) {
00433         if (ratio <= 1.0)
00434             break;
00435         ratio /= 2.0;
00436     }
00437     *plevel = i;
00438 
00439     return 0;
00440 }
00441 
00442 
00443 /*!
00444  *  pmsGetLevelForDealloc()
00445  *
00446  *      Input: data (ptr to memory chunk)
00447  *             &level (<return> level in memory store; -1 if allocated
00448  *                     outside the store)
00449  *      Return: 0 if OK, 1 on error
00450  */
00451 l_int32
00452 pmsGetLevelForDealloc(void     *data,
00453                       l_int32  *plevel)
00454 {
00455 l_int32           i;
00456 l_uint32         *first;
00457 L_PIX_MEM_STORE  *pms;
00458 
00459     PROCNAME("pmsGetLevelForDealloc");
00460 
00461     if (!plevel)
00462         return ERROR_INT("&level not defined", procName, 1);
00463     *plevel = -1;
00464     if (!data)
00465         return ERROR_INT("data not defined", procName, 1);
00466     if ((pms = CustomPMS) == NULL)
00467         return ERROR_INT("pms not defined", procName, 1);
00468 
00469     if (data < (void *)pms->baseptr || data >= (void *)pms->maxptr)
00470         return 0;   /*  -1  */
00471 
00472     for (i = 1; i < pms->nlevels; i++) {
00473         first = pms->firstptr[i];
00474         if (data < (void *)first)
00475             break;
00476     }
00477     *plevel = i - 1;
00478 
00479     return 0;
00480 }
00481 
00482 
00483 /*!
00484  *  pmsLogInfo()
00485  *
00486  *      Input:  (none)
00487  *      Return: void
00488  */
00489 void
00490 pmsLogInfo()
00491 {
00492 l_int32           i;
00493 L_PIX_MEM_STORE  *pms;
00494 
00495     if ((pms = CustomPMS) == NULL)
00496         return;
00497 
00498     fprintf(stderr, "Total number of pix used at each level\n");
00499     for (i = 0; i < pms->nlevels; i++)
00500          fprintf(stderr, " Level %d (%ld bytes): %d\n", i, pms->sizes[i],
00501                  pms->memused[i]);
00502 
00503     fprintf(stderr, "Max number of pix in use at any time in each level\n");
00504     for (i = 0; i < pms->nlevels; i++)
00505          fprintf(stderr, " Level %d (%ld bytes): %d\n", i, pms->sizes[i],
00506                  pms->memmax[i]);
00507 
00508     fprintf(stderr, "Number of pix alloc'd because none were available\n");
00509     for (i = 0; i < pms->nlevels; i++)
00510          fprintf(stderr, " Level %d (%ld bytes): %d\n", i, pms->sizes[i],
00511                  pms->memempty[i]);
00512 
00513     return;
00514 }
00515 
00516 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines