2 * Copyright (C) 2016 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #ifndef PLATFORM_WINDOWS
27 #include "pbd/reallocpool.h"
29 #ifdef RAP_WITH_SEGMENT_STATS
31 #define STATS_segment collect_segment_stats();
38 #ifdef RAP_WITH_CALL_STATS
39 #define STATS_inc(VAR) ++VAR;
40 #define STATS_if(COND, VAR) if (COND) {++VAR;}
41 #define STATS_used(DELTA) { _cur_used += (DELTA); if (_cur_used > _max_used) { _max_used = _cur_used; } }
43 #define STATS_inc(VAR)
44 #define STATS_if(COND, VAR)
45 #define STATS_used(DELTA)
48 #ifdef RAP_WITH_HISTOGRAM
49 #define STATS_hist(VAR, SIZE) ++VAR[hist_bin(SIZE)];
51 #define STATS_hist(VAR, SIZE)
56 typedef int poolsize_t;
58 ReallocPool::ReallocPool (std::string name, size_t bytes)
62 #ifdef RAP_WITH_SEGMENT_STATS
71 #ifdef RAP_WITH_CALL_STATS
82 _pool = (char*) ::malloc (bytes);
84 memset (_pool, 0, bytes); // make resident
85 #ifndef PLATFORM_WINDOWS
89 poolsize_t *in = (poolsize_t*) _pool;
90 *in = - (bytes - sizeof (poolsize_t));
93 #ifdef RAP_WITH_HISTOGRAM
94 for (int i = 0; i < RAP_WITH_HISTOGRAM; ++i) {
95 _hist_alloc[i] = _hist_free[i] = _hist_grow[i] = _hist_shrink[i] = 0;
100 ReallocPool::~ReallocPool ()
108 // realloc() does it all, malloc(), realloc() and free()
110 ReallocPool::_realloc (void *ptr, size_t oldsize, size_t newsize) {
112 ASSERT (!ptr || oldsize <= _asize (ptr));
113 oldsize = _asize (ptr); // ignore provided oldsize
115 if (ptr == 0 && newsize == 0) {
121 rv = _malloc (newsize);
122 STATS_if (!rv, _n_oom);
124 STATS_hist(_hist_alloc, newsize);
130 STATS_hist(_hist_free, _asize(ptr));
137 if (newsize == oldsize) {
138 ASSERT (_asize (ptr) <= newsize);
143 if (newsize > oldsize) {
145 const size_t ns = (newsize + RAP_BLOCKSIZE) & (~RAP_BLOCKSIZE);
146 if (ns <= _asize(ptr)) {
151 if ((rv = _malloc (newsize))) {
152 memcpy (rv, ptr, oldsize);
157 STATS_if(!rv, _n_oom);
159 STATS_hist(_hist_grow, newsize);
164 if (newsize < oldsize) {
165 ASSERT (_asize (ptr) >= newsize);
167 if ((rv = _malloc (newsize))) {
168 memccpy (rv, ptr, newsize);
170 STATS_if(!rv, _n_oom);
172 #elif 1 // shrink current segment
173 const size_t ns = (newsize + RAP_BLOCKSIZE) & (~RAP_BLOCKSIZE);
179 STATS_inc(_n_shrink);
180 STATS_hist (_hist_shrink, newsize);
184 return NULL; // not reached
187 #define SEGSIZ (*((poolsize_t*) p))
190 ReallocPool::consolidate_ptr (char *p) {
191 const poolsize_t sop = sizeof(poolsize_t);
192 if (p - SEGSIZ + sop >= _pool + _poolsize) {
193 return; // reached end
195 poolsize_t *next = (poolsize_t*)(p - SEGSIZ + sop);
197 SEGSIZ = SEGSIZ + (*next) - sop;
198 if (p - SEGSIZ + sop >= _pool + _poolsize) {
201 next = (poolsize_t*)(p -SEGSIZ + sop);
207 ReallocPool::_malloc (size_t s) {
208 const poolsize_t sop = sizeof(poolsize_t);
209 size_t traversed = 0;
213 s = (s + RAP_BLOCKSIZE) & (~RAP_BLOCKSIZE); // optional, helps to reduce fragmentation
216 while (1) { // iterates at most once over the available pool
217 ASSERT (SEGSIZ != 0);
219 traversed += SEGSIZ + sop;
220 if (traversed >= _poolsize) {
221 return NULL; // reached last segment. OOM.
224 if (p == _pool + _poolsize) {
229 // found free segment.
230 const poolsize_t avail = -SEGSIZ;
231 const poolsize_t sp = (poolsize_t)s;
232 const poolsize_t ss = sop + s;
242 // segment is larger than required space,
243 // (we need to fit data + two non-zero poolsize_t)
244 SEGSIZ = sp; // mark area as used.
245 *((poolsize_t*)(p + ss)) = ss - avail; // mark free space after.
246 consolidate_ptr (p + ss);
252 // segment is not large enough
253 consolidate_ptr (p); // try to consolidate with next segment (if any)
255 // check segment (again) and skip over too small free ones
256 while (SEGSIZ < 0 && (-SEGSIZ) <= ss && (-SEGSIZ) != sp) {
257 traversed += -SEGSIZ + sop;
258 if (traversed >= _poolsize) {
259 return NULL; // reached last segment. OOM.
261 p += (-SEGSIZ) + sop;
262 if (p >= _pool + _poolsize) {
264 if (SEGSIZ < 0) consolidate_ptr (p);
272 ReallocPool::_free (void *ptr) {
273 poolsize_t *in = (poolsize_t*) ptr;
275 *in = -*in; // mark as free
281 ReallocPool::_shrink (void *ptr, size_t newsize) {
282 poolsize_t *in = (poolsize_t*) ptr;
284 const poolsize_t avail = *in;
285 const poolsize_t ss = newsize + sizeof(poolsize_t);
287 return; // can't shrink
289 const poolsize_t sp = (poolsize_t)newsize;
290 STATS_used (newsize - avail);
291 *in = sp; // set new size
292 char *p = (char*) in;
293 *((poolsize_t*)(p + ss)) = ss - avail; // mark free space after.
298 ReallocPool::_asize (void *ptr) {
299 if (ptr == 0) return 0;
300 poolsize_t *in = (poolsize_t*) ptr;
309 ReallocPool::printstats ()
311 #ifdef RAP_WITH_SEGMENT_STATS
312 printf ("ReallocPool '%s': used: %ld (%.1f%%) (max: %ld), free: %ld [bytes]\n"
313 "|| segments: cur: %ld (max: %ld), largest-used: %ld, largest-free: %ld\n",
315 _cur_allocated, _cur_allocated * 100.f / _poolsize, _max_allocated, _cur_avail,
316 _seg_cur_count, _seg_max_count, _seg_max_used, _seg_max_avail);
317 #elif defined RAP_WITH_CALL_STATS
318 printf ("ReallocPool '%s':\n", _name.c_str());
320 #ifdef RAP_WITH_CALL_STATS
321 printf("|| malloc(): %ld, free(): %ld, realloc()+:%ld, realloc()-: %ld NOOP:%ld OOM:%ld\n",
322 _n_alloc, _n_free, _n_grow, _n_shrink, _n_noop, _n_oom);
323 printf("|| used: %ld / %ld, max: %ld (%.1f%%)\n",
324 _cur_used, _poolsize,
325 _max_used, 100.f *_max_used / _poolsize);
327 #ifdef RAP_WITH_HISTOGRAM
328 printf("--- malloc()\n");
329 print_histogram (_hist_alloc);
330 printf("--- realloc()/grow-to\n");
331 print_histogram (_hist_grow);
332 printf("--- realloc()/shrink-to\n");
333 print_histogram (_hist_shrink);
334 printf("--- free() histogram\n");
335 print_histogram (_hist_free);
336 printf("--------------------\n");
341 ReallocPool::dumpsegments ()
344 const poolsize_t sop = sizeof(poolsize_t);
345 poolsize_t *in = (poolsize_t*) p;
346 unsigned int traversed = 0;
347 #ifdef RAP_WITH_CALL_STATS
350 printf ("<<<<< %s\n", _name.c_str());
353 printf ("0x%08x used %4d\n", traversed, *in);
354 printf ("0x%08x data %p\n", traversed + sop , p + sop);
355 traversed += *in + sop;
357 #ifdef RAP_WITH_CALL_STATS
360 } else if ((*in) < 0) {
361 printf ("0x%08x free %4d [+%d]\n", traversed, -*in, sop);
362 traversed += -*in + sop;
365 printf ("0x%08x Corrupt!\n", traversed);
368 in = (poolsize_t*) p;
369 if (p == _pool + _poolsize) {
370 printf ("%08x end\n", traversed);
373 if (p > _pool + _poolsize) {
374 printf ("%08x Beyond End!\n", traversed);
378 #ifdef RAP_WITH_CALL_STATS
379 ASSERT (_cur_used == used);
384 #ifdef RAP_WITH_SEGMENT_STATS
386 ReallocPool::collect_segment_stats ()
389 poolsize_t *in = (poolsize_t*) p;
391 _cur_allocated = _cur_avail = 0;
392 _seg_cur_count = _seg_max_avail = _seg_max_used = 0;
397 _cur_allocated += *in;
399 if (*in > (poolsize_t)_seg_max_used) {
405 if (-*in > (poolsize_t)_seg_max_avail) {
406 _seg_max_avail = -*in;
409 p += sizeof(poolsize_t);
410 in = (poolsize_t*) p;
411 if (p == _pool + _poolsize) {
415 _seg_cur_count = _seg_cur_count;
417 if (_cur_allocated > _max_allocated) {
418 _max_allocated = _cur_allocated;
420 if (_seg_cur_count > _seg_max_count) {
421 _seg_max_count = _seg_cur_count;
426 #ifdef RAP_WITH_HISTOGRAM
428 ReallocPool::print_histogram (size_t const * const histogram) const
431 for (int i = 0; i < RAP_WITH_HISTOGRAM; ++i) {
432 if (histogram[i] > maxhist) maxhist = histogram[i];
434 const int termwidth = 50;
436 const int fact = RAP_BLOCKSIZE + 1;
438 if (maxhist > 0) for (int i = 0; i < RAP_WITH_HISTOGRAM; ++i) {
439 if (histogram[i] == 0) { continue; }
440 if (i == RAP_WITH_HISTOGRAM -1 ) {
442 printf(" > %4d: %7lu ", i * fact, histogram[i]);
444 printf(">%4d:%7lu ", i * fact, histogram[i]);
448 printf("%4d .. %4d: %7lu ", i * fact, (i + 1) * fact -1, histogram[i]);
450 printf("%4d: %7lu ", i * fact, (i + 1) * fact -1 , histogram[i]);
453 int bar_width = (histogram[i] * termwidth ) / maxhist;
454 if (bar_width == 0 && histogram[i] > 0) bar_width = 1;
455 for (int j = 0; j < bar_width; ++j) printf("#");
461 ReallocPool::hist_bin (size_t s) const {
463 s = (s + RAP_BLOCKSIZE) & (~RAP_BLOCKSIZE);
464 s /= (RAP_BLOCKSIZE + 1);
466 if (s > RAP_WITH_HISTOGRAM - 1) s = RAP_WITH_HISTOGRAM - 1;