1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * Lifetime-based fast allocation, inspired by much prior art, including
8 * "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
9 * David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
21 static PLArenaStats
*arena_stats_list
;
23 #define COUNT(pool,what) (pool)->stats.what++
25 #define COUNT(pool,what) /* nothing */
28 #define PL_ARENA_DEFAULT_ALIGN sizeof(double)
30 PR_IMPLEMENT(void) PL_InitArenaPool(
31 PLArenaPool
*pool
, const char *name
, PRUint32 size
, PRUint32 align
)
34 * Look-up table of PR_BITMASK(PR_CeilingLog2(align)) values for
37 static const PRUint8 pmasks
[33] = {
39 0, 1, 3, 3, 7, 7, 7, 7,15,15,15,15,15,15,15,15, /* 1 ... 16 */
40 31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31 /* 17 ... 32 */
44 align
= PL_ARENA_DEFAULT_ALIGN
;
47 if (align
< sizeof(pmasks
)/sizeof(pmasks
[0])) {
48 pool
->mask
= pmasks
[align
];
51 pool
->mask
= PR_BITMASK(PR_CeilingLog2(align
));
54 pool
->first
.next
= NULL
;
55 /* Set all three addresses in pool->first to the same dummy value.
56 * These addresses are only compared with each other, but never
58 pool
->first
.base
= pool
->first
.avail
= pool
->first
.limit
=
59 (PRUword
)PL_ARENA_ALIGN(pool
, &pool
->first
+ 1);
60 pool
->current
= &pool
->first
;
62 * Compute the net size so that each arena's gross size is |size|.
63 * sizeof(PLArena) + pool->mask is the header and alignment slop
64 * that PL_ArenaAllocate adds to the net size.
66 if (size
> sizeof(PLArena
) + pool
->mask
) {
67 pool
->arenasize
= size
- (sizeof(PLArena
) + pool
->mask
);
70 pool
->arenasize
= size
;
73 memset(&pool
->stats
, 0, sizeof pool
->stats
);
74 pool
->stats
.name
= strdup(name
);
75 pool
->stats
.next
= arena_stats_list
;
76 arena_stats_list
= &pool
->stats
;
82 ** PL_ArenaAllocate() -- allocate space from an arena pool
84 ** Description: PL_ArenaAllocate() allocates space from an arena
87 ** First, try to satisfy the request from arenas starting at
88 ** pool->current. Then try to allocate a new arena from the heap.
90 ** Returns: pointer to allocated space or NULL
92 ** Notes: The original implementation had some difficult to
93 ** solve bugs; the code was difficult to read. Sometimes it's
94 ** just easier to rewrite it. I did that. larryh.
96 ** See also: bugzilla: 45343.
100 PR_IMPLEMENT(void *) PL_ArenaAllocate(PLArenaPool
*pool
, PRUint32 nb
)
103 char *rp
; /* returned pointer */
106 PR_ASSERT((nb
& pool
->mask
) == 0);
109 nb
= (PRUword
)PL_ARENA_ALIGN(pool
, nb
); /* force alignment */
114 /* attempt to allocate from arenas at pool->current */
118 if ( nb
<= a
->limit
- a
->avail
) {
120 rp
= (char *)a
->avail
;
124 } while( NULL
!= (a
= a
->next
) );
127 /* attempt to allocate from the heap */
129 PRUint32 sz
= PR_MAX(pool
->arenasize
, nb
);
130 if (PR_UINT32_MAX
- sz
< sizeof *a
+ pool
->mask
) {
133 sz
+= sizeof *a
+ pool
->mask
; /* header and alignment slop */
134 a
= (PLArena
*)PR_MALLOC(sz
);
137 a
->limit
= (PRUword
)a
+ sz
;
138 a
->base
= a
->avail
= (PRUword
)PL_ARENA_ALIGN(pool
, a
+ 1);
139 PL_MAKE_MEM_NOACCESS((void*)a
->avail
, a
->limit
- a
->avail
);
140 rp
= (char *)a
->avail
;
142 PR_ASSERT(a
->avail
<= a
->limit
);
143 /* the newly allocated arena is linked after pool->current
144 * and becomes pool->current */
145 a
->next
= pool
->current
->next
;
146 pool
->current
->next
= a
;
148 if ( NULL
== pool
->first
.next
) {
149 pool
->first
.next
= a
;
151 PL_COUNT_ARENA(pool
,++);
152 COUNT(pool
, nmallocs
);
157 /* we got to here, and there's no memory to allocate */
159 } /* --- end PL_ArenaAllocate() --- */
161 PR_IMPLEMENT(void *) PL_ArenaGrow(
162 PLArenaPool
*pool
, void *p
, PRUint32 size
, PRUint32 incr
)
166 if (PR_UINT32_MAX
- size
< incr
) {
169 PL_ARENA_ALLOCATE(newp
, pool
, size
+ incr
);
171 memcpy(newp
, p
, size
);
176 PR_IMPLEMENT(void) PL_ClearArenaPool(PLArenaPool
*pool
, PRInt32 pattern
)
180 for (a
= pool
->first
.next
; a
; a
= a
->next
) {
181 PR_ASSERT(a
->base
<= a
->avail
&& a
->avail
<= a
->limit
);
183 PL_CLEAR_UNUSED_PATTERN(a
, pattern
);
184 PL_MAKE_MEM_NOACCESS((void*)a
->avail
, a
->limit
- a
->avail
);
189 * Free tail arenas linked after head, which may not be the true list head.
190 * Reset pool->current to point to head in case it pointed at a tail arena.
192 static void FreeArenaList(PLArenaPool
*pool
, PLArena
*head
)
194 PLArena
*a
= head
->next
;
205 PL_COUNT_ARENA(pool
,--);
209 pool
->current
= head
;
212 PR_IMPLEMENT(void) PL_ArenaRelease(PLArenaPool
*pool
, char *mark
)
216 for (a
= &pool
->first
; a
; a
= a
->next
) {
217 if (PR_UPTRDIFF(mark
, a
->base
) <= PR_UPTRDIFF(a
->avail
, a
->base
)) {
218 a
->avail
= (PRUword
)PL_ARENA_ALIGN(pool
, mark
);
219 FreeArenaList(pool
, a
);
225 PR_IMPLEMENT(void) PL_FreeArenaPool(PLArenaPool
*pool
)
227 FreeArenaList(pool
, &pool
->first
);
228 COUNT(pool
, ndeallocs
);
231 PR_IMPLEMENT(void) PL_FinishArenaPool(PLArenaPool
*pool
)
233 FreeArenaList(pool
, &pool
->first
);
236 PLArenaStats
*stats
, **statsp
;
238 if (pool
->stats
.name
) {
239 PR_DELETE(pool
->stats
.name
);
241 for (statsp
= &arena_stats_list
; (stats
= *statsp
) != 0;
242 statsp
= &stats
->next
) {
243 if (stats
== &pool
->stats
) {
244 *statsp
= stats
->next
;
252 PR_IMPLEMENT(void) PL_CompactArenaPool(PLArenaPool
*ap
)
256 PR_IMPLEMENT(void) PL_ArenaFinish(void)
260 PR_IMPLEMENT(size_t) PL_SizeOfArenaPoolExcludingPool(
261 const PLArenaPool
*pool
, PLMallocSizeFn mallocSizeOf
)
264 * The first PLArena is within |pool|, so don't measure it. Subsequent
265 * PLArenas are separate and must be measured.
268 const PLArena
*arena
= pool
->first
.next
;
270 size
+= mallocSizeOf(arena
);
277 PR_IMPLEMENT(void) PL_ArenaCountAllocation(PLArenaPool
*pool
, PRUint32 nb
)
279 pool
->stats
.nallocs
++;
280 pool
->stats
.nbytes
+= nb
;
281 if (nb
> pool
->stats
.maxalloc
) {
282 pool
->stats
.maxalloc
= nb
;
284 pool
->stats
.variance
+= nb
* nb
;
287 PR_IMPLEMENT(void) PL_ArenaCountInplaceGrowth(
288 PLArenaPool
*pool
, PRUint32 size
, PRUint32 incr
)
290 pool
->stats
.ninplace
++;
293 PR_IMPLEMENT(void) PL_ArenaCountGrowth(
294 PLArenaPool
*pool
, PRUint32 size
, PRUint32 incr
)
296 pool
->stats
.ngrows
++;
297 pool
->stats
.nbytes
+= incr
;
298 pool
->stats
.variance
-= size
* size
;
300 if (size
> pool
->stats
.maxalloc
) {
301 pool
->stats
.maxalloc
= size
;
303 pool
->stats
.variance
+= size
* size
;
306 PR_IMPLEMENT(void) PL_ArenaCountRelease(PLArenaPool
*pool
, char *mark
)
308 pool
->stats
.nreleases
++;
311 PR_IMPLEMENT(void) PL_ArenaCountRetract(PLArenaPool
*pool
, char *mark
)
313 pool
->stats
.nfastrels
++;
319 PR_IMPLEMENT(void) PL_DumpArenaStats(FILE *fp
)
322 double mean
, variance
;
324 for (stats
= arena_stats_list
; stats
; stats
= stats
->next
) {
325 if (stats
->nallocs
!= 0) {
326 mean
= (double)stats
->nbytes
/ stats
->nallocs
;
327 variance
= fabs(stats
->variance
/ stats
->nallocs
- mean
* mean
);
332 fprintf(fp
, "\n%s allocation statistics:\n", stats
->name
);
333 fprintf(fp
, " number of arenas: %u\n", stats
->narenas
);
334 fprintf(fp
, " number of allocations: %u\n", stats
->nallocs
);
335 fprintf(fp
, " number of free arena reclaims: %u\n", stats
->nreclaims
);
336 fprintf(fp
, " number of malloc calls: %u\n", stats
->nmallocs
);
337 fprintf(fp
, " number of deallocations: %u\n", stats
->ndeallocs
);
338 fprintf(fp
, " number of allocation growths: %u\n", stats
->ngrows
);
339 fprintf(fp
, " number of in-place growths: %u\n", stats
->ninplace
);
340 fprintf(fp
, "number of released allocations: %u\n", stats
->nreleases
);
341 fprintf(fp
, " number of fast releases: %u\n", stats
->nfastrels
);
342 fprintf(fp
, " total bytes allocated: %u\n", stats
->nbytes
);
343 fprintf(fp
, " mean allocation size: %g\n", mean
);
344 fprintf(fp
, " standard deviation: %g\n", sqrt(variance
));
345 fprintf(fp
, " maximum allocation size: %u\n", stats
->maxalloc
);
348 #endif /* PL_ARENAMETER */