3 * $Id: sub.c,v 1.4 1999/05/13 22:48:55 mdw Exp $
5 * Allocation of known-size blocks
7 * (c) 1998 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of the mLib utilities library.
14 * mLib is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
19 * mLib is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
24 * You should have received a copy of the GNU Library General Public
25 * License along with mLib; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
30 /*----- Revision history --------------------------------------------------*
33 * Revision 1.4 1999/05/13 22:48:55 mdw
34 * Change `-ise' to `-ize' throughout.
36 * Revision 1.3 1999/05/06 19:51:35 mdw
37 * Reformatted the LGPL notice a little bit.
39 * Revision 1.2 1999/05/05 18:50:31 mdw
40 * Change licensing conditions to LGPL.
42 * Revision 1.1.1.1 1998/06/17 23:44:42 mdw
43 * Initial version of mLib
47 /*----- The big idea ------------------------------------------------------*
49 * This file provides an extra layer over @malloc@. It provides fast
50 * turnover for small blocks, and tries to minimize the per-block overhead.
52 * To do its job, @alloc@ must place an extra restriction on you: you must
53 * know the size of a block when you free it. Usually you'll have this
54 * information encoded in some way either in the block or in the thing that
55 * referenced it, so this isn't a hardship.
57 * It works fairly simply. If a request for a big block (as defined by the
58 * constants below) comes in, it gets sent on to @malloc@ unmolested. For
59 * small blocks, it goes straight to a `bin' -- a list containing free blocks
60 * of exactly that size, or the nearest bigger size we can manage. If the
61 * bin is empty, a `chunk' is allocated from @malloc@: this has enough room
62 * for lots of blocks of the requested size, so it ets split up and each
63 * individual small block is added to the bin list. The first block in the
64 * bin list is then removed and given to the caller. In this way, @malloc@
65 * only stores its information once for lots of little blocks, so we save
66 * memory. Because I know where the correct bin is just from the block size,
67 * and I don't need to do any searching at all in the usual case (because the
68 * list isn't empty) I can get a speed advantage too.
70 * This code is almost certainly not ANSI conformant, although I'm not
71 * actually sure. If some kind soul would let me know how seriously I've
72 * violated the standard, and whether this is easily fixable, I'd be
76 /*----- Header files ------------------------------------------------------*/
78 /* --- ANSI headers --- */
84 /* --- Local headers --- */
86 #undef TRACK_ENABLE /* Can't track suballoc routines */
89 /*----- Configuration and tuning ------------------------------------------*/
91 /* --- The largest block I'll handle here --- *
93 * Anything larger will be handed on to @malloc@.
96 #define SUB_MAXBIN 256
98 /* --- Preferred chunk size --- *
100 * When a bin is empty, I'll allocate a large chunk of approximately this
101 * size and divvy it up into small bin-sized blocks.
104 #define SUB_CHUNK 4096
106 /*----- Other useful macros -----------------------------------------------*/
108 /* --- The granularity of bin buffers --- *
110 * All blocks allocated by the binner are a multiple of this size. I've
111 * chosen @void *@ because I need to store @void *@ things in here.
114 #define SUB_GRANULE sizeof(void *)
116 /* --- Finding the right bin for a given size --- *
118 * This chooses the correct bin for an allocation. Input is the size of
119 * block wanted; result is the bin index.
122 #define SUB_BIN(x) (((x) + SUB_GRANULE - 1) / SUB_GRANULE)
124 /* --- Convert a bin back to the block size --- *
126 * This gives the size of block contained in a given bin.
129 #define SUB_BINSZ(x) ((x) * SUB_GRANULE)
131 /* --- Number of bins required --- */
133 #define SUB_BINS (SUB_MAXBIN / SUB_GRANULE + 1)
135 /*----- Static variables --------------------------------------------------*/
137 static void *sub__bins[SUB_BINS];
138 static size_t sub__sizes[SUB_BINS];
140 /*----- Main code ---------------------------------------------------------*/
142 /* --- @sub_alloc@ --- *
144 * Arguments: @size_t s@ = size of chunk wanted
146 * Returns: Pointer to a block at least as large as the one wanted.
148 * Use: Allocates a small block of memory. If there is no more
149 * memory left, the exception @EXC_NOMEM@ is raised.
152 void *sub_alloc(size_t s)
154 int bin = SUB_BIN(s);
157 /* --- Handle oversize blocks --- */
162 /* --- If the bin is empty, find some memory --- */
164 if (!sub__bins[bin]) {
167 p = xmalloc(sub__sizes[bin]);
168 q = p + sub__sizes[bin];
183 /* --- Extract the first block in the list --- */
186 sub__bins[bin] = *(void **)p;
190 /* --- @sub_free@ --- *
192 * Arguments: @void *p@ = address of block to free
193 * @size_t s@ = size of block
197 * Use: Frees a block allocated by @sub_alloc@.
200 void sub_free(void *p, size_t s)
202 int bin = SUB_BIN(s);
207 *(void **)p = sub__bins[bin];
212 /* --- @sub_init@ --- *
218 * Use: Initializes the magic allocator.
225 /* --- Initialize the sizes bins --- */
227 for (i = 1; i < SUB_BINS; i++) {
228 sub__sizes[i] = ((SUB_CHUNK + SUB_BINSZ(i) - 1) /
229 SUB_BINSZ(i) * SUB_BINSZ(i));
233 /*----- Debugging code ----------------------------------------------------*/
238 #define SIZE_MAX 2048
239 #define ITERATIONS 500000
243 static void *block[BLOCKS];
244 static size_t size[BLOCKS];
251 for (count = 0; count < ITERATIONS; count++) {
254 sub_free(block[i], size[i]);
258 block[i] = sub_alloc(size[i] =
259 rand() % (SUB_MAXBIN - 128) + 128);
261 memset(block[i], 0, size[i]); /* trample allocated storage */
270 /*----- That's all, folks -------------------------------------------------*/