1 |
/* |
2 |
* Ascend Pooled Memory Manager |
3 |
* by Benjamin Andrew Allan |
4 |
* Created: 2/96 |
5 |
* Version: $Revision: 1.1 $ |
6 |
* Version control file: $RCSfile: pool.h,v $ |
7 |
* Date last modified: $Date: 1997/07/18 11:37:02 $ |
8 |
* Last modified by: $Author: mthomas $ |
9 |
* |
10 |
* This file is part of the Ascend Language Interpreter. |
11 |
* |
12 |
* Copyright (C) 1996 Benjamin Andrew Allan |
13 |
* |
14 |
* The Ascend Language Interpreter is free software; you can redistribute |
15 |
* it and/or modify it under the terms of the GNU 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. |
18 |
* |
19 |
* The Ascend Language Interpreter is distributed in hope that it will be |
20 |
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
22 |
* General Public License for more details. |
23 |
* |
24 |
* You should have received a copy of the GNU General Public License |
25 |
* along with the program; if not, write to the Free Software Foundation, |
26 |
* Inc., 675 Mass Ave, Cambridge, MA 02139 USA. Check the file named |
27 |
* COPYING. |
28 |
* |
29 |
*/ |
30 |
|
31 |
/** @file |
32 |
* Ascend Pooled Memory Manager. |
33 |
* <pre> |
34 |
* Contents: Pool Memory module |
35 |
* |
36 |
* Authors: Benjamin Andrew Allan |
37 |
* |
38 |
* Dates: 05/95 - original version: utilities/mem.[ch] |
39 |
* nearly generic element allocation routines. |
40 |
* 02/96 - copied to compiler and swapped pool for mem in |
41 |
* names. deleted all the Karl foo of mem.c. |
42 |
* 03/96 - retuned some of the parameters for compiler. |
43 |
* |
44 |
* Description: This generic element memory manager is yet another |
45 |
* implementation of a fixed byte size malloc system |
46 |
* that is more efficient than most standard mallocs. This |
47 |
* is possible because we make the fixed size assumption. |
48 |
* The idea is clearly not original. |
49 |
* For best results, you must understand your memory |
50 |
* usage pattern and tune the pool_create_store() parameters |
51 |
* accordingly. |
52 |
* |
53 |
* This has similar functionality to free_store.[ch] but |
54 |
* is a rather more efficient implementation with a better |
55 |
* interface. The mem_ implementation of this code |
56 |
* remains in utilities/ because the solvers (which may |
57 |
* also stand alone) need to be able to link without the |
58 |
* compiler goop. |
59 |
* |
60 |
* Detailed Description: |
61 |
* |
62 |
* The following definitions provide a generic and reasonably efficient memory |
63 |
* allocation system for situations where many many objects of the same size |
64 |
* need to be "allocated and deallocated" rapidly and stored efficiently: |
65 |
* that is to say when normal malloc is too slow and expensive. |
66 |
* This scheme does not play reference count games. Instead any elements that |
67 |
* have been created and then used and subsequently "freed" go on a list |
68 |
* and are handed out again at the next opportunity. The list is run LIFO. |
69 |
* The list is associated with the pool_store_t. |
70 |
* |
71 |
* There is one restriction on the elements: they will be a multiple of the |
72 |
* size of a pointer, even if you specify otherwise. If this is too large, |
73 |
* write your own allocator. Think about how your elements align. On many |
74 |
* architectures doubles should only be stored at addresses that are |
75 |
* multiples of 8 bytes. |
76 |
* There is the implicit restriction on a store that it must contain |
77 |
* no more than MAXINT elements. If this is too small, write your own |
78 |
* allocator. |
79 |
* |
80 |
* Even More Details |
81 |
* |
82 |
* If you specify an element size that is not a nice multiple of your machine |
83 |
* word length, you are *very* likely to get data alignment (bus) errors. |
84 |
* E.g., if your element is not a convenient multiple of |
85 |
* sizeof(double) in size, double data are likely to die. |
86 |
* |
87 |
* The allocation scheme looks like so: |
88 |
* [struct pool_store_header | ] |
89 |
* __________________| |
90 |
* | |
91 |
* V |
92 |
* pool _____________________________________________________ |
93 |
* | b | b | b | b | b | b | b | b | b | b | b | b | b | |
94 |
* ----------------------------------------------------- |
95 |
* where each b is a pointer to an array of size 'width' of elements. |
96 |
* |
97 |
* The size characteristics of this scheme are tunable at run time so that |
98 |
* it can be scaled well when the number of elements and likely amount |
99 |
* of expansion required are known. |
100 |
* |
101 |
* When #including pool.h, make sure these files are #included first: |
102 |
* #include <stdio.h> |
103 |
* #include "utilities/ascConfig.h" |
104 |
* #include "compiler.h" |
105 |
* </pre> |
106 |
*/ |
107 |
|
108 |
#ifndef __pool_h_seen__ |
109 |
#define __pool_h_seen__ |
110 |
|
111 |
typedef struct pool_store_header *pool_store_t; |
112 |
/**< |
113 |
* The token for this memory system. Internal details of the |
114 |
* implementation are private. Do not dereference or free this |
115 |
* pointer. |
116 |
*/ |
117 |
|
118 |
/** |
119 |
* Memory pool statistics data structure. |
120 |
* This is the reporting structure for a pool_store_header query. |
121 |
*/ |
122 |
struct pool_statistics { |
123 |
double p_eff; /**< bytes in use / bytes allocated */ |
124 |
double p_recycle; /**< avg reuses per element */ |
125 |
int elt_total; /**< current elements existing in store*/ |
126 |
int elt_taken; /**< fresh elements handed out */ |
127 |
int elt_inuse; /**< elements the user currently has */ |
128 |
int elt_onlist; /**< elements awaiting reuse */ |
129 |
int elt_size; /**< bytes/element, as mem sees it */ |
130 |
int str_len; /**< length of active pool. */ |
131 |
int str_wid; /**< elements/pointer in pool. */ |
132 |
}; |
133 |
|
134 |
extern void pool_get_stats(struct pool_statistics *p_stats, |
135 |
pool_store_t ps); |
136 |
/**< |
137 |
* Get statistics about a pool store. |
138 |
* Stuffs the user's interface structure, p_stats, with info |
139 |
* derived from ps given. |
140 |
* If pool_LIGHTENING (see below) is TRUE, no statistics |
141 |
* except elt_total, elt_taken, elt_onlist, and elt_size are |
142 |
* available. |
143 |
* |
144 |
* @param p_stats Pointer to a pool_statistics struct to receive |
145 |
* the info. If p_stats is NULL, an error message |
146 |
* is printed and the function returns. |
147 |
* @param ps Pointer to the pool store to query. |
148 |
*/ |
149 |
|
150 |
extern pool_store_t pool_create_store(int length, |
151 |
int width, |
152 |
size_t eltsize, |
153 |
int deltalen, |
154 |
int deltapool); |
155 |
/**< |
156 |
* Creates and returns a new pool store. The returned pool_store_t |
157 |
* contains all the necessary accounting information, but in particular |
158 |
* the eltsize is fixed at creation. All elements requested from ps |
159 |
* will be pointers to eltsize bytes of memory. |
160 |
* Returns NULL if a store of the requested length*width*eltsize |
161 |
* cannot be initially allocated.<br><br> |
162 |
* |
163 |
* The user may request more than length*width elements from the store: |
164 |
* this will cause it to grow. It will grow (internally) in chunks of |
165 |
* deltalen*width elements. The pool vector above grows in chunks of |
166 |
* deltapool, the extra pointers in it being NULL until needed.<br><br> |
167 |
* |
168 |
* @param length The initial number of width*eltsize blocks that the |
169 |
* new pool store will contain. If length < 1, an error |
170 |
* message is printed and NULL is returned. |
171 |
* |
172 |
* @param width Number of elements in each block. |
173 |
* Width should (for some architectures) be such that |
174 |
* width*eltsize = 2^n - 32 for some n fairly large |
175 |
* (heuristic: n= 9..13). Widths that are too large may |
176 |
* be prone to causing excess page faults, though the |
177 |
* process cpu time reported by the clock() can be much |
178 |
* better for extremely large sizes. If width < 1, an |
179 |
* error message is printed and NULL is returned.<br><br> |
180 |
* Widths that are too small will result in an excessive |
181 |
* number of pool expansions, which may severely limit |
182 |
* performance on some VM systems. See deltapool below |
183 |
* about pool expansions.<br><br> |
184 |
* If you know something about the page size of your |
185 |
* architecture, fiddling with width may help you reduce |
186 |
* your page fault or cache miss count in some uses. |
187 |
* |
188 |
* @param eltsize Element size maintained by the pool. |
189 |
* For maximum efficiency, eltsize should be an integer |
190 |
* multiple of sizeof(void *). If it is not, elts will be |
191 |
* padded so that this is the case. This is to avoid |
192 |
* pointer data misalignment. This restriction may or |
193 |
* may not help avoid alignment problems with items inside |
194 |
* the user's element structure. |
195 |
* |
196 |
* @param deltalen Number of additional pointers in the pool that will be |
197 |
* allocated when more elements are needed than are |
198 |
* available internally. deltalen must be at least 1 or |
199 |
* creation of the new pool will fail. |
200 |
* |
201 |
* @param deltapool Size change of the pool array when expanded. It should |
202 |
* be as large as you are willing to tolerate. The pool |
203 |
* array starts out completely filled (all pointers allocated). |
204 |
* When the pool needs more pointers it gets them in chunks of |
205 |
* at least deltapool. These additional pointers will not |
206 |
* automatically have elements allocated to them; rather, |
207 |
* they will be initialized to NULL and filled in only as the |
208 |
* chunks of deltalen*width elements are required. |
209 |
|
210 |
* @return A pointer to the newly created pool store, NULL if an error occurred. |
211 |
*/ |
212 |
|
213 |
extern void *pool_get_element(pool_store_t ps); |
214 |
/**< |
215 |
* Get a usable element from the pool. |
216 |
* Returns a void pointer to a blob of memory of the eltsize |
217 |
* set when ps was created. You must cast it appropriately. |
218 |
* The blob data is not initialized in any particular way. |
219 |
* |
220 |
* @param ps The pool store from which to retrieve an element. |
221 |
* If ps is NULL, then an error message is printed |
222 |
* and NULL is returned. |
223 |
* @return A pointer to the usable element, or NULL iff ps is NULL or |
224 |
* store growth is required and the operating system is unable |
225 |
* to allocate the required memory. |
226 |
*/ |
227 |
|
228 |
extern void pool_get_element_list(pool_store_t ps, |
229 |
int len, |
230 |
void **ellist); |
231 |
/**< |
232 |
* NOT IMPLEMENTED. |
233 |
* |
234 |
* Takes the pointer array, ellist, of length len provided by the user |
235 |
* and fills it with pointers to elements from the store. |
236 |
* There is not necessarily any relation (memory map wise) between the |
237 |
* locations pointed to by successive entries in the ellist returned. |
238 |
* Ellist should point to an array with enough space for len pointers. |
239 |
* Returns NULL in ellist[0] iff store growth is required and the operating |
240 |
* system is unable to allocate the required memory.<br><br> |
241 |
* |
242 |
* The user is reminded that if he knows how many elements he needs |
243 |
* ahead of time, he is probably better off mallocing the array himself. |
244 |
* |
245 |
* @todo Implement general/pool.c:pool_get_element_list() of remove |
246 |
* it from pool.h. |
247 |
*/ |
248 |
|
249 |
#define pool_DEBUG FALSE |
250 |
/**< |
251 |
* Flag controlling extra checking of the pool management routines. |
252 |
* Setting pool_DEBUG to TRUE causes the pool_store routines to do |
253 |
* some RATHER expensive checking. It should be set to FALSE. |
254 |
*/ |
255 |
#define pool_LIGHTENING FALSE |
256 |
/**< |
257 |
* Flag controlling extent of internal sanity checking. |
258 |
* Setting pool_LIGHTENING to TRUE causes pool_store routines to assume |
259 |
* the user is perfect: i.e. no sanity checks are at all necessary and most |
260 |
* internal accounting can be disabled. No one with an ounce of sanity |
261 |
* would ever set this flag to TRUE unless the code using the |
262 |
* pool module was proven bug free. It makes the allocator smaller |
263 |
* and faster, though, by ~15%. <br><br> |
264 |
* |
265 |
* This flag exists to make it easy to test the theory that the |
266 |
* accounting overhead in this code is not of significant cost. |
267 |
* Below 1e5 elements it really isn't bad enough to justify the |
268 |
* assumption that the user is perfect. |
269 |
*/ |
270 |
|
271 |
#if pool_DEBUG |
272 |
#define pool_free_element(ps,eltpointer) pool_free_elementF((ps),(eltpointer),__FILE__) |
273 |
#else |
274 |
#define pool_free_element(ps,eltpointer) pool_free_elementF((ps),(eltpointer)) |
275 |
#endif |
276 |
/**< |
277 |
* Releases an element back to the store. |
278 |
* If you return the same pointer twice, we will have |
279 |
* no qualms about returning it to you twice. We won't necessarily |
280 |
* return it to you twice, though.<br><br> |
281 |
* |
282 |
* If pool_DEBUG is TRUE, eltpointer will be checked for belonging |
283 |
* to ps. If you call pool_free_element() with a pointer the ps does |
284 |
* not recognize, it will not be freed and a message will be |
285 |
* sent to ASCERR.<br><br> |
286 |
* |
287 |
* If pool_DEBUG is FALSE, eltpointer will be assumed to belong |
288 |
* with the ps in question. The implications of handing |
289 |
* pool_free_element() an element of the wrong size or from the |
290 |
* wrong ps (bearing in mind the LIFO reuse of elements) should be |
291 |
* obvious. If they are not, stop using these routines.<br><br> |
292 |
* |
293 |
* If at any time the number of elements freed exceeds the number |
294 |
* handed out, we will whine (unless pool_LIGHTENING). If ps is |
295 |
* NULL, and error message is printed and the function returns. |
296 |
* If eltpointer is NULL, we will ignore it completely. |
297 |
* |
298 |
* @param ps pool_store_t, the pool store to modify. |
299 |
* @param eltpointer void*, the element to return to the pool. |
300 |
* @return No return value. |
301 |
* @see pool_free_elementF() |
302 |
*/ |
303 |
|
304 |
extern void pool_free_elementF(pool_store_t ps, void * eltpointer |
305 |
#if pool_DEBUG |
306 |
,CONST char *file |
307 |
#endif |
308 |
); |
309 |
/**< |
310 |
* Implementation function for pool_free_element(). |
311 |
* Do not call this function directly - use pool_free_element() instead. |
312 |
*/ |
313 |
|
314 |
#if pool_DEBUG |
315 |
#define pool_clear_store(ps) pool_clear_storeF((ps),__FILE__) |
316 |
#else |
317 |
#define pool_clear_store(ps) pool_clear_storeF(ps) |
318 |
#endif |
319 |
/**< |
320 |
* Clears the books in ps. That is, we reset the ps to think |
321 |
* that __all__ elements are freshly available and have never |
322 |
* been handed out. If ps is NULL, an error message is printed |
323 |
* and the function returns.<br><br> |
324 |
* |
325 |
* If pool_DEBUG is TRUE, it first verifies that all elements have |
326 |
* been pool_freed first and whines if not. |
327 |
* Get and free calls will be balanced to see if spurious elements |
328 |
* have been handed in. (This is a heuristic check). |
329 |
* The clear process will cause any spurious pointers that were |
330 |
* turned in via pool_free_element() to be forgotten about.<br><br> |
331 |
* |
332 |
* Clearing a store is not necessary for pool_destroy_store(). |
333 |
* Recycling is faster from the recycle list than from a cleared store, ~2%. |
334 |
* Clear is provided for users who want to obtain elements with a higher |
335 |
* probability of successive elements being near each other. |
336 |
* |
337 |
* @param ps pool_store_t, the pool store to clear. |
338 |
* @return No return value. |
339 |
* @see pool_clear_storeF() |
340 |
*/ |
341 |
|
342 |
extern void pool_clear_storeF(pool_store_t ps |
343 |
#if pool_DEBUG |
344 |
, CONST char *file |
345 |
#endif |
346 |
); |
347 |
/**< |
348 |
* Implementation function for pool_clear_store(). |
349 |
* Do not call this function directly - use pool_clear_store() instead. |
350 |
*/ |
351 |
|
352 |
extern void pool_destroy_store(pool_store_t ps); |
353 |
/**< |
354 |
* Deallocates everything associated with the ps. |
355 |
* If pool_DEBUG is TRUE, it first verifies that all elements |
356 |
* have been pool_freed first and whines if not. |
357 |
* If pool_DEBUG is FALSE, just nukes everything unconditionally. |
358 |
* If ps is NULL, an error message is printed and the function |
359 |
* returns. |
360 |
* |
361 |
* @param ps The pool store to destroy. |
362 |
*/ |
363 |
|
364 |
extern void pool_print_store(FILE *fp, pool_store_t ps, unsigned detail); |
365 |
/**< |
366 |
* Prints statistics about a pool_store_t to the file stream given. |
367 |
* Which stats get printed depends on detail. |
368 |
* - If detail 0, displays just summary statistics. |
369 |
* - If detail 1, just internal statistics. |
370 |
* - If detail >1, displays both. |
371 |
* |
372 |
* @param fp The open file stream on which to print the report. |
373 |
* @param ps The pool store on which to report. |
374 |
* @param detail The level of detail to print: |
375 |
* 0 = summary, 1 = internal stats, >1 = both. |
376 |
*/ |
377 |
|
378 |
extern size_t pool_sizeof_store(pool_store_t ps); |
379 |
/**< |
380 |
* Retrieves the current total byte usage of the store. |
381 |
* Returns 0 if an invalid pool store is specified. |
382 |
* |
383 |
* @param ps pool_store_t, the pool store to query. |
384 |
* @return The total bytes currently used by the pool store. |
385 |
*/ |
386 |
|
387 |
#endif /* __pool_h_seen__ */ |
388 |
|