Skip to content

Commit bdaaf60

Browse files
committed
F_MALLOC: Add LHS coalescing on free() operations
This is a continuation/rework of commit fb9a377, which intended to address the de-fragmentation issues where performance would drop. Unfortunately, there were still memory allocation / releasing patterns which still lead to heavy fragmentation. For example, an MI "dlg_list" command, which allocates fragments from Left -> Right as the dialog list is walked: Chunk-1 Chunk-2 Chunk-3 ... Chunk-N After building the MI output, the chunks were also freed Left -> Right. Due to the former allocator logic of RHS-only coalescing, the coalescing would never happen! In some cases, this could lead to fragmentation of an indefinite amount of memory, given enough time (e.g., even 8 GB PKG). This patch adds LHS coalescing to F_MALLOC, such that when "Chunk-2" is freed, in the above example, the allocator tries to merge it with both "Chunk-1" and "Chunk-3" before exiting the free() function (depending on the state of chunks 1 and 3, this could mean 0, 1 or 2 coalescings). This F_MALLOC patch was stress-tested using the mem/test/ testing suite. Many thanks to @fedkis and @ankogan for helping troubleshoot the issue and also test the current version of this patch! Fixes #2726
1 parent 8be5364 commit bdaaf60

File tree

3 files changed

+33
-5
lines changed

3 files changed

+33
-5
lines changed

mem/f_malloc.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <string.h>
2727
#include <stdio.h>
2828
#include <stdlib.h>
29+
#include <assert.h>
2930

3031
#include "f_malloc.h"
3132
#include "../dprint.h"
@@ -42,6 +43,7 @@
4243
#define FRAG_OVERHEAD (sizeof(struct fm_frag))
4344
#define frag_is_free(_f) ((_f)->prev)
4445

46+
#define FRAG_PREV(f) ((f)->pf)
4547
#define FRAG_NEXT(f) \
4648
((struct fm_frag *)(void *)((char *)(f) + sizeof(struct fm_frag) + (f)->size))
4749

@@ -226,10 +228,14 @@ struct fm_block *fm_malloc_init(char *address, unsigned long size, char *name)
226228
/* init initial fragment*/
227229
fm->first_frag->size=size-init_overhead;
228230
fm->last_frag->size=0;
231+
fm->last_frag->pf=fm->first_frag;
229232

230233
fm->last_frag->prev=NULL;
231234
fm->first_frag->prev=NULL;
232235

236+
assert(((char *)fm->first_frag + sizeof *fm->first_frag + fm->first_frag->size)
237+
== (char *)fm->last_frag);
238+
233239
/* link initial fragment into the free list*/
234240

235241
fm_insert_free(fm, fm->first_frag);

mem/f_malloc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ struct fm_frag {
6464
long reserved;
6565
} u;
6666
struct fm_frag **prev;
67+
68+
struct fm_frag *pf; /* previous "physical" frag */
69+
6770
#ifdef DBG_MALLOC
6871
const char *file;
6972
const char *func;

mem/f_malloc_dyn.h

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ void fm_split_frag(struct fm_block *fm, struct fm_frag *frag,
5858
/*split the fragment*/
5959
n=FRAG_NEXT(frag);
6060
n->size=rest-FRAG_OVERHEAD;
61+
n->pf=frag;
62+
FRAG_NEXT(n)->pf=n;
6163

6264
/*
6365
* The real used memory does not increase, as the frag memory is not
@@ -217,7 +219,7 @@ void fm_free(struct fm_block *fm, void *p, const char *file,
217219
const char *func, unsigned int line)
218220
#endif
219221
{
220-
struct fm_frag *f, *n;
222+
struct fm_frag *f, *neigh;
221223

222224
#ifdef DBG_MALLOC
223225
LM_GEN1(memlog, "%s_free(%p), called from %s: %s(%d)\n", fm->name, p, file,
@@ -242,18 +244,34 @@ void fm_free(struct fm_block *fm, void *p, const char *file,
242244
#endif
243245

244246
/* attempt to join with a next fragment that also happens to be free */
245-
n = FRAG_NEXT(f);
246-
if (((char*)n < (char*)fm->last_frag) && frag_is_free(n)) {
247-
fm_remove_free(fm, n);
247+
neigh = FRAG_NEXT(f);
248+
if (((char*)neigh < (char*)fm->last_frag) && frag_is_free(neigh)) {
249+
fm_remove_free(fm, neigh);
248250
/* join */
249-
f->size += n->size + FRAG_OVERHEAD;
251+
f->size += neigh->size + FRAG_OVERHEAD;
252+
FRAG_NEXT(neigh)->pf = f;
250253

251254
#if defined(DBG_MALLOC) || defined(STATISTICS)
252255
//fm->real_used -= FRAG_OVERHEAD;
253256
fm->used += FRAG_OVERHEAD;
254257
#endif
255258
}
256259

260+
/* attempt to join with a prev fragment that also happens to be free */
261+
neigh = FRAG_PREV(f);
262+
if (neigh && frag_is_free(neigh)) {
263+
fm_remove_free(fm, neigh);
264+
neigh->size += f->size + FRAG_OVERHEAD;
265+
FRAG_NEXT(f)->pf = neigh;
266+
267+
#if defined(DBG_MALLOC) || defined(STATISTICS)
268+
//fm->real_used -= FRAG_OVERHEAD;
269+
fm->used += FRAG_OVERHEAD;
270+
#endif
271+
272+
f = neigh;
273+
}
274+
257275
#ifdef DBG_MALLOC
258276
f->file = file;
259277
f->func = func;
@@ -354,6 +372,7 @@ void *fm_realloc(struct fm_block *fm, void *p, unsigned long size,
354372
fm_remove_free(fm,n);
355373
/* join */
356374
f->size += n->size + FRAG_OVERHEAD;
375+
FRAG_NEXT(f)->pf = f;
357376

358377
#if defined(DBG_MALLOC) || defined(STATISTICS)
359378
//fm->real_used -= FRAG_OVERHEAD;

0 commit comments

Comments
 (0)