Skip to content

Commit

Permalink
F_MALLOC: Add LHS coalescing on free() operations
Browse files Browse the repository at this point in the history
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

(cherry picked from commit bdaaf60)
  • Loading branch information
liviuchircu committed Apr 19, 2023
1 parent 1bef009 commit 74237cb
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 5 deletions.
6 changes: 6 additions & 0 deletions mem/f_malloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "f_malloc.h"
#include "../dprint.h"
Expand All @@ -40,6 +41,7 @@
#define FRAG_OVERHEAD (sizeof(struct fm_frag))
#define frag_is_free(_f) ((_f)->prev)

#define FRAG_PREV(f) ((f)->pf)
#define FRAG_NEXT(f) \
((struct fm_frag *)((char *)(f) + sizeof(struct fm_frag) + (f)->size))

Expand Down Expand Up @@ -216,10 +218,14 @@ struct fm_block *fm_malloc_init(char *address, unsigned long size, char *name)
/* init initial fragment*/
fm->first_frag->size=size-init_overhead;
fm->last_frag->size=0;
fm->last_frag->pf=fm->first_frag;

fm->last_frag->prev=NULL;
fm->first_frag->prev=NULL;

assert(((char *)fm->first_frag + sizeof *fm->first_frag + fm->first_frag->size)
== (char *)fm->last_frag);

/* link initial fragment into the free list*/

fm_insert_free(fm, fm->first_frag);
Expand Down
3 changes: 3 additions & 0 deletions mem/f_malloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ struct fm_frag {
long reserved;
} u;
struct fm_frag **prev;

struct fm_frag *pf; /* previous "physical" frag */

#ifdef DBG_MALLOC
const char *file;
const char *func;
Expand Down
29 changes: 24 additions & 5 deletions mem/f_malloc_dyn.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ void fm_split_frag(struct fm_block *fm, struct fm_frag *frag,
/*split the fragment*/
n=FRAG_NEXT(frag);
n->size=rest-FRAG_OVERHEAD;
n->pf=frag;
FRAG_NEXT(n)->pf=n;

/*
* The real used memory does not increase, as the frag memory is not
Expand Down Expand Up @@ -217,7 +219,7 @@ void fm_free(struct fm_block *fm, void *p, const char *file,
const char *func, unsigned int line)
#endif
{
struct fm_frag *f, *n;
struct fm_frag *f, *neigh;

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

/* attempt to join with a next fragment that also happens to be free */
n = FRAG_NEXT(f);
if (((char*)n < (char*)fm->last_frag) && frag_is_free(n)) {
fm_remove_free(fm, n);
neigh = FRAG_NEXT(f);
if (((char*)neigh < (char*)fm->last_frag) && frag_is_free(neigh)) {
fm_remove_free(fm, neigh);
/* join */
f->size += n->size + FRAG_OVERHEAD;
f->size += neigh->size + FRAG_OVERHEAD;
FRAG_NEXT(neigh)->pf = f;

#if defined(DBG_MALLOC) || defined(STATISTICS)
//fm->real_used -= FRAG_OVERHEAD;
fm->used += FRAG_OVERHEAD;
#endif
}

/* attempt to join with a prev fragment that also happens to be free */
neigh = FRAG_PREV(f);
if (neigh && frag_is_free(neigh)) {
fm_remove_free(fm, neigh);
neigh->size += f->size + FRAG_OVERHEAD;
FRAG_NEXT(f)->pf = neigh;

#if defined(DBG_MALLOC) || defined(STATISTICS)
//fm->real_used -= FRAG_OVERHEAD;
fm->used += FRAG_OVERHEAD;
#endif

f = neigh;
}

#ifdef DBG_MALLOC
f->file = file;
f->func = func;
Expand Down Expand Up @@ -354,6 +372,7 @@ void *fm_realloc(struct fm_block *fm, void *p, unsigned long size,
fm_remove_free(fm,n);
/* join */
f->size += n->size + FRAG_OVERHEAD;
FRAG_NEXT(f)->pf = f;

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

0 comments on commit 74237cb

Please sign in to comment.