[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [patches] malloc & madvise
- To: "patches@xxxxxxxxxx" <patches@xxxxxxxxxx>
- Subject: Re: [patches] malloc & madvise
- From: Richard Kralovic <Richard.Kralovic@xxxxxxxxxxx>
- Date: Mon, 16 Nov 2009 22:13:00 +0100
> I'm certain some form of this should be acceptable. The simplest would be
Great :-).
> It would also be worthwhile considering whether the performance cost from
> this can be reduced - for example, should it only be done after a few
You are perfectly right here; the patch I posted was more like a proof
of concept. Nevertheless, any more sophisticated way of returning the
memory back to OS involves bigger changes in the malloc code.
I tried to implement the following approach: Malloc keeps track about
each block of consecutive free pages that have not yet been returned to
OS. In each free chunk there is a doubly linked list of such blocks,
sorted according to the block addresses. Furthermore, there is one
central doubly linked list of all such blocks sorted according to the
creation time. Once the total free space in the blocks exceeds
M_TRIM_THRESHOLD, some of the blocks are returned to OS via MADV_DONTNEED.
In the current implementation (as attached), there is no runtime switch
for turning on the new behaviour (it can be, however, disabled at
compilation time - if USE_MADVISE is not defined, the patch does
nothing). Hence, there is some overhead from the bookkeeping of the free
blocks. Nevertheless, this overhead should be minimal: All operations
are amortized O(1) time and I have not been able to reliably observe an
increase in the userspace running time in my preliminary tests. Calling
madvise may cause a more significant overhead. This can be, however,
tuned by the MALLOC_TRIM_THRESHOLD_ variable (and the corresponding
mallopt call). Hence, the modified malloc should not be slower than the
original malloc if the original one is lucky enough to have zero
fragmentation.
If you think this approach is acceptable, I'll proceed with the
copyright paperwork and do some more tests in meantime. All comments are
welcome.
> may be a completely useless heuristic. But there must be malloc
> benchmarks, and studies of what is or is not a good heuristic for malloc,
> out there.)
Unfortunately, I am not an expert here. I was able to find very few
malloc benchmarks during my quick search, basically ebizzy was the only
one I found reasonably useful. Nevertheless, I'll try to do some more
tests of the modified malloc.
Best regards
Richard
diff -ur eglibc-2.10.1/malloc/malloc.c eglibc-2.10.1.new/malloc/malloc.c
--- eglibc-2.10.1/malloc/malloc.c 2009-07-29 18:29:54.000000000 +0200
+++ eglibc-2.10.1.new/malloc/malloc.c 2009-11-16 20:47:46.854169521 +0100
@@ -838,6 +838,13 @@
#include "/usr/include/malloc.h"
#endif
+/*
+ When defined, we use madvise(MADV_DONTNEED) to tell kernel about blocks
+ of consecutive free pages. This involves keeping track of such blocks
+ which where not yet returned to system. If the size of such blocks
+ exceeds trim_threshold, we call madvise on them.
+*/
+#define USE_MADVISE
/* ---------- description of public routines ------------ */
@@ -1778,6 +1785,23 @@
----------------------- Chunk representations -----------------------
*/
+#ifdef USE_MADVISE
+/* This represents a block of consecutive free pages that can be returned
+ to the system via madvise. */
+struct free_block {
+ /* Lenght of the block */
+ INTERNAL_SIZE_T size;
+
+ /* Circular doubly linked list within the chunk. */
+ struct free_block *bk_chunk, *fd_chunk;
+
+ /* Circular doubly linked list of blocks sorted by creation time. */
+ struct free_block *bk_time, *fd_time;
+};
+
+/* End of the given free_block. */
+#define BLOCK_END(p) ((void *)(p) + (p)->size)
+#endif
/*
This struct declaration is misleading (but accurate and necessary).
@@ -1796,8 +1820,14 @@
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
-};
+#ifdef USE_MADVISE
+ /* This is valid only if the chunk is free, not top, not mmapped and
+ large enough (as defined by CHUNK_IS_LARGE(). In that case, only
+ fd_chunk and bk_chunk are relevant. */
+ struct free_block free_blocks;
+#endif
+};
/*
malloc_chunk details:
@@ -2142,6 +2172,7 @@
#define NSMALLBINS 64
#define SMALLBIN_WIDTH MALLOC_ALIGNMENT
#define MIN_LARGE_SIZE (NSMALLBINS * SMALLBIN_WIDTH)
+/* The code for USE_MADVISE assumes MIN_LARGE_SIZE < page size! */
#define in_smallbin_range(sz) \
((unsigned long)(sz) < (unsigned long)MIN_LARGE_SIZE)
@@ -2375,6 +2406,15 @@
/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
+
+#ifdef USE_MADVISE
+ /* Total number of bytes in pages that can be returned to system
+ via MADV_DONTNEED. */
+ INTERNAL_SIZE_T can_free;
+
+ /* Circular doubly-linked list of free blocks */
+ struct free_block free_blocks;
+#endif
};
struct malloc_par {
@@ -2422,6 +2462,32 @@
static struct malloc_par mp_;
+#ifdef USE_MADVISE
+/* Helper functions, should be inlined. */
+static void *page_round_down(void *p)
+{
+ const size_t psm1 = mp_.pagesize-1;
+ return (void *)((uintptr_t)p & ~psm1);
+}
+
+static void *page_round_up(void *p)
+{
+ const size_t psm1 = mp_.pagesize-1;
+ return (void *)(((uintptr_t)p+psm1) & ~psm1);
+}
+
+/* First possible position of a free page within a chunk */
+#define CHUNK_START_FREE(p) page_round_up(((void *)p) + sizeof(struct malloc_chunk))
+
+/* End of the last page within a chunk */
+#define CHUNK_END_FREE(p) page_round_down(((void *)p) + chunksize(p))
+
+/* Test if the chunk is large enough to hold a circular list of free
+ blocks. */
+#define CHUNK_IS_LARGE(p) (chunksize(p) > mp_.pagesize)
+#define CHUNKSIZE_IS_LARGE(p) ((p) > mp_.pagesize)
+#endif
+
#ifdef PER_THREAD
/* Non public mallopt parameters. */
@@ -2467,6 +2533,12 @@
av->flags |= FASTCHUNKS_BIT;
av->top = initial_top(av);
+
+#ifdef USE_MADVISE
+ /* Initialize an empty circular list of free blocks */
+ av->free_blocks.fd_time = av->free_blocks.bk_time = &av->free_blocks;
+ av->can_free = 0;
+#endif
}
/*
@@ -2562,6 +2634,9 @@
#define check_remalloced_chunk(A,P,N)
#define check_malloced_chunk(A,P,N)
#define check_malloc_state(A)
+#ifdef USE_MADVISE
+#define check_free_blocks(A)
+#endif
#else
@@ -2571,6 +2646,9 @@
#define check_remalloced_chunk(A,P,N) do_check_remalloced_chunk(A,P,N)
#define check_malloced_chunk(A,P,N) do_check_malloced_chunk(A,P,N)
#define check_malloc_state(A) do_check_malloc_state(A)
+#ifdef USE_MADVISE
+#define check_free_blocks(A) do_check_free_blocks(A)
+#endif
/*
Properties of all chunks
@@ -2760,6 +2838,45 @@
assert(prev_inuse(p));
}
+#ifdef USE_MADVISE
+/*
+ Properties of free blocks.
+*/
+
+static void do_check_free_blocks(mstate av)
+{
+ int i, n;
+ INTERNAL_SIZE_T total_free;
+ mchunkptr p,b;
+ struct free_block *f;
+
+ total_free = 0;
+ n = 0;
+
+ /* Iterate over all chunks */
+ for (i = 1; i < NBINS; ++i) {
+ b = bin_at(av,i);
+ for (p = last(b); p != b; p = p->bk) {
+ if (CHUNK_IS_LARGE(p))
+ for (f = p->free_blocks.fd_chunk; f != &p->free_blocks; f = f->fd_chunk) {
+ assert(!(f->size & 1));
+ total_free += f->size;
+ f->size |= 1; /* mark */
+ n++;
+ }
+ }
+ }
+ assert(total_free == av->can_free);
+
+ /* Iterate over the time list */
+ for (f = av->free_blocks.fd_time; f != &av->free_blocks; f = f->fd_time) {
+ assert(f->size & 1); /* check mark */
+ f->size &= ~1; /* unmark */
+ n--;
+ }
+ assert(!n);
+}
+#endif
/*
Properties of malloc_state.
@@ -2809,7 +2926,7 @@
max_fast_bin = fastbin_index(get_max_fast ());
for (i = 0; i < NFASTBINS; ++i) {
- p = av->fastbins[i];
+ p = av->fastbinsY[i];
/* The following test can only be performed for the main arena.
While mallopt calls malloc_consolidate to get rid of all fast
@@ -3488,6 +3605,46 @@
return 0;
}
+#ifdef USE_MADVISE
+/*
+ madvise_trim gives back free blocks, until their total size does not
+ exceed threshold. It is called from _int_free, malloc_consolidate, and
+ mTRIm. Basically, it is an analogy of sYSMTRIm. A decent compiler should
+ inline it.
+*/
+static int
+internal_function
+#if __STD_C
+_madvise_trim(mstate av, INTERNAL_SIZE_T threshold)
+#else
+_madvise_trim(av, threshold) mstate av; INTERNAL_SIZE_T threshold;
+#endif
+{
+ int result = 0;
+ check_free_blocks(av);
+ while (av->can_free > threshold && av->free_blocks.fd_time != &av->free_blocks) {
+ result = 1;
+ struct free_block *p = av->free_blocks.fd_time;
+ INTERNAL_SIZE_T s = p->size;
+
+ /* update statistics */
+ av->can_free -= s;
+
+ /* unlink p from the list within chunk */
+ p->bk_chunk->fd_chunk = p->fd_chunk;
+ p->fd_chunk->bk_chunk = p->bk_chunk;
+
+ /* unlink p from the time list */
+ av->free_blocks.fd_time = p->fd_time;
+ p->fd_time->bk_time = &av->free_blocks;
+
+ madvise(p, s, MADV_DONTNEED);
+ }
+ check_free_blocks(av);
+ return result;
+}
+#endif
+
#ifdef HAVE_MMAP
static void
@@ -4220,6 +4377,91 @@
------------------------------ malloc ------------------------------
*/
+#ifdef USE_MADVISE
+
+/* This is a helper function for removing all free blocks from a chunk.
+ Should be inlined. */
+static void _exhaust_free_chunk(mstate av, mchunkptr victim)
+{
+ struct free_block *p;
+ /* Unlink free blocks that are in the allocated chunk. */
+ for ( p = victim->free_blocks.fd_chunk; p != &victim->free_blocks;
+ p = p->fd_chunk ) {
+ av->can_free -= p->size;
+ p->fd_time->bk_time = p->bk_time;
+ p->bk_time->fd_time = p->fd_time;
+ }
+}
+
+/* A helper function for adding a new free block to the time list. */
+static void _add_free_blocks(mstate av, struct free_block *f)
+{
+ f->bk_time = av->free_blocks.bk_time;
+ av->free_blocks.bk_time = f;
+ f->fd_time = &av->free_blocks;
+ f->bk_time->fd_time = f;
+}
+
+/* This handles the case when a free chunk is splitted.
+ victim has still its old size, remainder is uninitialized. */
+static void _split_free_chunk(mstate av, mchunkptr victim,
+ mchunkptr remainder, INTERNAL_SIZE_T remainder_size)
+{
+ /* If the victim is small, there is nothing to do. */
+ if (CHUNK_IS_LARGE(victim)) {
+ struct free_block *p;
+
+ /* Unlink free blocks that are in the allocated chunk. */
+ for ( p = victim->free_blocks.fd_chunk;
+ p != &victim->free_blocks &&
+ BLOCK_END(p) <= CHUNK_START_FREE(remainder);
+ p = p->fd_chunk ) {
+ av->can_free -= p->size;
+ p->fd_time->bk_time = p->bk_time;
+ p->bk_time->fd_time = p->fd_time;
+ }
+
+ /* If the remainder is small, it can not contain any free pages, so all
+ free blocks of victim have been unlinked and we are done. */
+ if (CHUNKSIZE_IS_LARGE(remainder_size)) {
+ if ( p == &victim->free_blocks ) {
+ /* If there are no free blocks, just initialize an empty list. */
+ remainder->free_blocks.fd_chunk =
+ remainder->free_blocks.bk_chunk = &remainder->free_blocks;
+ remainder->free_blocks.size = 0;
+ } else {
+ /* Calculate the first free page in remainder. */
+ struct free_block *f = CHUNK_START_FREE(remainder);
+ if (p >= f) {
+ f = p;
+ } else {
+ /* Unlink p from time list... */
+ p->bk_time->fd_time = p->fd_time;
+ p->fd_time->bk_time = p->bk_time;
+
+ /* ...and add f there. */
+ _add_free_blocks(av, f);
+
+ /* initialize size of f */
+ f->size = p->size - ((void *)f-(void *)p);
+ av->can_free -= ((void *)f-(void *)p);
+
+ /* p is replaced by f in the list within chunk */
+ f->fd_chunk = p->fd_chunk;
+ p->fd_chunk->bk_chunk = f;
+ }
+
+ remainder->free_blocks.size = 0;
+ remainder->free_blocks.fd_chunk = f;
+ f->bk_chunk = &remainder->free_blocks;
+ remainder->free_blocks.bk_chunk = victim->free_blocks.bk_chunk;
+ remainder->free_blocks.bk_chunk->fd_chunk = &remainder->free_blocks;
+ }
+ }
+ }
+}
+#endif
+
static Void_t*
_int_malloc(mstate av, size_t bytes)
{
@@ -4297,6 +4539,8 @@
anyway, so we can check now, which is faster.)
*/
+ /* Since small bins are always too small to hold free blocks, we do not
+ need to handle them here. */
if (in_smallbin_range(nb)) {
idx = smallbin_index(nb);
bin = bin_at(av,idx);
@@ -4338,6 +4582,10 @@
malloc_consolidate(av);
}
+#ifdef USE_MADVISE
+ check_free_blocks(av);
+#endif
+
/*
Process recently freed or remaindered chunks, taking one only if
it is exact fit, or, if this a small request, the chunk is remainder from
@@ -4378,6 +4626,9 @@
/* split and reattach remainder */
remainder_size = size - nb;
remainder = chunk_at_offset(victim, nb);
+#ifdef USE_MADVISE
+ _split_free_chunk(av, victim, remainder, remainder_size);
+#endif
unsorted_chunks(av)->bk = unsorted_chunks(av)->fd = remainder;
av->last_remainder = remainder;
remainder->bk = remainder->fd = unsorted_chunks(av);
@@ -4406,6 +4657,9 @@
/* Take now instead of binning if exact fit */
if (size == nb) {
+#ifdef USE_MADVISE
+ if (CHUNK_IS_LARGE(victim)) _exhaust_free_chunk(av, victim);
+#endif
set_inuse_bit_at_offset(victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
@@ -4507,10 +4761,16 @@
set_inuse_bit_at_offset(victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
+#ifdef USE_MADVISE
+ if (CHUNK_IS_LARGE(victim)) _exhaust_free_chunk(av, victim);
+#endif
}
/* Split */
else {
remainder = chunk_at_offset(victim, nb);
+#ifdef USE_MADVISE
+ _split_free_chunk(av, victim, remainder, remainder_size);
+#endif
/* We cannot assume the unsorted list is empty and therefore
have to perform a complete insert here. */
bck = unsorted_chunks(av);
@@ -4600,12 +4860,17 @@
set_inuse_bit_at_offset(victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
+#ifdef USE_MADVISE
+ if (CHUNK_IS_LARGE(victim)) _exhaust_free_chunk(av, victim);
+#endif
}
/* Split */
else {
remainder = chunk_at_offset(victim, nb);
-
+#ifdef USE_MADVISE
+ _split_free_chunk(av, victim, remainder, remainder_size);
+#endif
/* We cannot assume the unsorted list is empty and therefore
have to perform a complete insert here. */
bck = unsorted_chunks(av);
@@ -4710,6 +4975,85 @@
/*
------------------------------ free ------------------------------
*/
+#ifdef USE_MADVISE
+/*
+ This does the bookkeeping of free blocks after we have freed a chunk p.
+ Chunk p might have been consolidated. Meaning of the parameters is:
+ inited - chunk p have initialized free_blocks
+ start_free...end_free - upper bound on the newly obtained free blocks
+ right_head, right_tail - free block list of the consolidated right
+ neigbor, or null if none.
+*/
+static void _consolidate_free(mstate av, mchunkptr p, int inited,
+ void *start_free, void *end_free, struct free_block *right_head,
+ struct free_block *right_tail)
+{
+ /* If p is small, there is nothing to do. */
+ if (CHUNK_IS_LARGE(p)) {
+ struct free_block *start, *end;
+
+ /* Calculate the range that can be freed. */
+ start = CHUNK_START_FREE(p);
+ if ((void *)start < start_free) start = start_free;
+ end = CHUNK_END_FREE(p);
+ if ((void *)end > end_free) end = end_free;
+
+ /* Initialize free block list in p. */
+ if (!inited) {
+ p->free_blocks.fd_chunk = p->free_blocks.bk_chunk = &p->free_blocks;
+ p->free_blocks.size = 0;
+ }
+
+ if (end > start) {
+ av->can_free += ((void *)end)-((void *)start);
+ /* Consolidate new block with last block of p if possible. */
+ if ( BLOCK_END(p->free_blocks.bk_chunk) == start )
+ p->free_blocks.bk_chunk->size += ((void *)end)-((void *)start);
+ else {
+ /* Add a new block to the list of p */
+ start->size = ((void *)end)-((void *)start);
+
+ /* add to chunk list */
+ start->bk_chunk = p->free_blocks.bk_chunk;
+ start->fd_chunk = &p->free_blocks;
+ p->free_blocks.bk_chunk = start;
+ start->bk_chunk->fd_chunk = start;
+
+ /* add to time list */
+ _add_free_blocks(av, start);
+ }
+ }
+
+ if (right_head) {
+ if ( BLOCK_END(p->free_blocks.bk_chunk) == right_head ) {
+ /* We need to merge the last block of p with the first block from
+ right list. */
+
+ /* Unlink right_head from time list. */
+ right_head->fd_time->bk_time = right_head->bk_time;
+ right_head->bk_time->fd_time = right_head->fd_time;
+
+ p->free_blocks.bk_chunk->size += right_head->size;
+
+ /* Throw away the merged block. */
+ if (right_head != right_tail)
+ right_head = right_head->fd_chunk;
+ else
+ right_head = NULL;
+ }
+ }
+
+ /* If there are still some blocks to be added. */
+ if (right_head) {
+ right_head->bk_chunk = p->free_blocks.bk_chunk;
+ right_tail->fd_chunk = &p->free_blocks;
+ p->free_blocks.bk_chunk->fd_chunk = right_head;
+ p->free_blocks.bk_chunk = right_tail;
+ }
+ }
+ check_free_blocks(av);
+}
+#endif
static void
#ifdef ATOMIC_FASTBINS
@@ -4733,6 +5077,9 @@
#endif
size = chunksize(p);
+#ifdef USE_MADVISE
+ check_free_blocks(av);
+#endif
/* Little security check which won't hurt performance: the
allocator never wrapps around at the end of the address space.
@@ -4844,6 +5191,12 @@
*/
else if (!chunk_is_mmapped(p)) {
+#ifdef USE_MADVISE
+ void *start_free, *end_free;
+ int initialized_blocks;
+ struct free_block *right_head, *right_tail;
+#endif
+
#ifdef ATOMIC_FASTBINS
if (! have_lock) {
# if THREAD_STATS
@@ -4862,6 +5215,14 @@
nextchunk = chunk_at_offset(p, size);
+#ifdef USE_MADVISE
+ /* calculate upper bound on the area that can be freed */
+ start_free = page_round_down(p);
+ end_free = CHUNK_START_FREE(nextchunk);
+ initialized_blocks = 0;
+ right_head = right_tail = NULL;
+#endif
+
/* Lightweight tests: check whether the block is already the
top block. */
if (__builtin_expect (p == av->top, 0))
@@ -4898,6 +5259,9 @@
/* consolidate backward */
if (!prev_inuse(p)) {
prevsize = p->prev_size;
+#ifdef USE_MADVISE
+ initialized_blocks = CHUNKSIZE_IS_LARGE(prevsize);
+#endif
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(p, bck, fwd);
@@ -4909,6 +5273,14 @@
/* consolidate forward */
if (!nextinuse) {
+#ifdef USE_MADVISE
+ /* If there are some free blocks in next chunk, save them. */
+ if (CHUNKSIZE_IS_LARGE(nextsize) &&
+ nextchunk->free_blocks.fd_chunk != &nextchunk->free_blocks) {
+ right_head = nextchunk->free_blocks.fd_chunk;
+ right_tail = nextchunk->free_blocks.bk_chunk;
+ }
+#endif
unlink(nextchunk, bck, fwd);
size += nextsize;
} else
@@ -4947,9 +5319,21 @@
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
+#ifdef USE_MADVISE
+ /* We do not want any free blocks in top. */
+ if (initialized_blocks) _exhaust_free_chunk(av, p);
+#endif
check_chunk(av, p);
}
+#ifdef USE_MADVISE
+ /* If we have consolidated into top, we are already done. Otherwise,
+ consolidate the free blocks in the modified chunk. */
+ if ( p != av->top)
+ _consolidate_free(av, p, initialized_blocks, start_free, end_free,
+ right_head, right_tail);
+#endif
+
/*
If freeing a large space, consolidate possibly-surrounding
chunks. Then, if the total unused topmost memory exceeds trim
@@ -4983,6 +5367,11 @@
}
}
+#ifdef USE_MADVISE
+ /* If necessary, give some memory back to system. */
+ _madvise_trim(av, mp_.trim_threshold);
+#endif
+
#ifdef ATOMIC_FASTBINS
if (! have_lock) {
assert (locked);
@@ -5040,6 +5429,12 @@
int nextinuse;
mchunkptr bck;
mchunkptr fwd;
+#ifdef USE_MADVISE
+ void *start_free, *end_free;
+ int initialized_blocks;
+ struct free_block *right_head, *right_tail;
+#endif
+
/*
If max_fast is 0, we know that av hasn't
@@ -5047,6 +5442,9 @@
*/
if (get_max_fast () != 0) {
+#ifdef USE_MADVISE
+ check_free_blocks(av);
+#endif
clear_fastchunks(av);
unsorted_bin = unsorted_chunks(av);
@@ -5088,8 +5486,19 @@
nextchunk = chunk_at_offset(p, size);
nextsize = chunksize(nextchunk);
+#ifdef USE_MADVISE
+ /* calculate upper bound on the area that can be freed */
+ start_free = page_round_down(p);
+ end_free = CHUNK_START_FREE(nextchunk);
+ initialized_blocks = 0;
+ right_head = right_tail = NULL;
+#endif
+
if (!prev_inuse(p)) {
prevsize = p->prev_size;
+#ifdef USE_MADVISE
+ initialized_blocks = CHUNKSIZE_IS_LARGE(prevsize);
+#endif
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(p, bck, fwd);
@@ -5099,6 +5508,14 @@
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
if (!nextinuse) {
+#ifdef USE_MADVISE
+ /* If there are some free blocks in next chunk, save them. */
+ if (CHUNKSIZE_IS_LARGE(nextsize) &&
+ nextchunk->free_blocks.fd_chunk != &nextchunk->free_blocks) {
+ right_head = nextchunk->free_blocks.fd_chunk;
+ right_tail = nextchunk->free_blocks.bk_chunk;
+ }
+#endif
size += nextsize;
unlink(nextchunk, bck, fwd);
} else
@@ -5123,8 +5540,20 @@
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
+#ifdef USE_MADVISE
+ /* We do not want any free blocks in top. */
+ if (initialized_blocks) _exhaust_free_chunk(av, p);
+#endif
}
+#ifdef USE_MADVISE
+ /* If we have consolidated into top, we are already done. Otherwise,
+ consolidate the free blocks in the modified chunk. */
+ if ( p != av->top)
+ _consolidate_free(av, p, initialized_blocks, start_free, end_free,
+ right_head, right_tail);
+#endif
+
} while ( (p = nextp) != 0);
}
@@ -5217,6 +5646,12 @@
(unsigned long)(nb)) {
newp = oldp;
unlink(next, bck, fwd);
+#ifdef USE_MADVISE
+ /* FIXME: This is suboptimal. We throw away all information about free
+ blocks in the chunk next, which can lead to calling madvise on
+ areas that already were freed. */
+ if (CHUNK_IS_LARGE(next)) _exhaust_free_chunk(av, next);
+#endif
}
/* allocate, copy, free */
@@ -5771,6 +6206,9 @@
/* Ensure initialization/consolidation */
malloc_consolidate (av);
+#ifdef USE_MADVISE
+ int result = _madvise_trim(av, pad);
+#else
const size_t ps = mp_.pagesize;
int psindex = bin_index (ps);
const size_t psm1 = ps - 1;
@@ -5812,6 +6250,7 @@
}
}
}
+#endif
#ifndef MORECORE_CANNOT_TRIM
return result | (av == &main_arena ? sYSTRIm (pad, av) : 0);