new slice allocator implementation.

Thu Dec  1 17:32:46 2005  Tim Janik  <timj@imendio.com>

        * glib/gslice.[hc]: new slice allocator implementation.

        * tests/slice-test.c: added random slice allocation test.

        * glib/gthread.[hc]: removed newly added private thread mem API.

        * glib/gthreadinit.h:
        * glib/gmessages.c:
        * glib/gthread.c:
        * glib/gmem.c: divided glib threading initialisation into three phases,
        initialisation where private keys and messaging are not available (only
        needed by gmem.c), initialisation without messaging but private keys
        available (gslice.c, gmessage.c), and full fledged initialisers that
        server the rest of glib. initialisation functions got renamed to reflect
        the limitations of their corresponding phases.

        * glib/gmem.c: removed memchunk code, defer allocations to
        g_slice_* instead.

        * glib/gmem.[hc]: removed g_slice_* skeletons.

        * glib/glib.symbols: added g_slice_* symbols.

        * configure.in: check for availability of posix_memalign(3), memalign(3)
        and valloc(3).

        * glib/Makefile.am: added gslice.[hc].
This commit is contained in:
Tim Janik 2005-12-01 16:34:33 +00:00 committed by Tim Janik
parent d871f19c30
commit 733b1789c1
16 changed files with 1406 additions and 760 deletions

View File

@ -1,3 +1,33 @@
Thu Dec 1 17:32:46 2005 Tim Janik <timj@imendio.com>
* glib/gslice.[hc]: new slice allocator implementation.
* tests/slice-test.c: added random slice allocation test.
* glib/gthread.[hc]: removed newly added private thread mem API.
* glib/gthreadinit.h:
* glib/gmessages.c:
* glib/gthread.c:
* glib/gmem.c: divided glib threading initialisation into three phases,
initialisation where private keys and messaging are not available (only
needed by gmem.c), initialisation without messaging but private keys
available (gslice.c, gmessage.c), and full fledged initialisers that
server the rest of glib. initialisation functions got renamed to reflect
the limitations of their corresponding phases.
* glib/gmem.c: removed memchunk code, defer allocations to
g_slice_* instead.
* glib/gmem.[hc]: removed g_slice_* skeletons.
* glib/glib.symbols: added g_slice_* symbols.
* configure.in: check for availability of posix_memalign(3), memalign(3)
and valloc(3).
* glib/Makefile.am: added gslice.[hc].
2005-12-01 Tor Lillqvist <tml@novell.com> 2005-12-01 Tor Lillqvist <tml@novell.com>
* glib/gstdio.c (g_stat): In the Win32 implementation, strip * glib/gstdio.c (g_stat): In the Win32 implementation, strip

View File

@ -1,3 +1,33 @@
Thu Dec 1 17:32:46 2005 Tim Janik <timj@imendio.com>
* glib/gslice.[hc]: new slice allocator implementation.
* tests/slice-test.c: added random slice allocation test.
* glib/gthread.[hc]: removed newly added private thread mem API.
* glib/gthreadinit.h:
* glib/gmessages.c:
* glib/gthread.c:
* glib/gmem.c: divided glib threading initialisation into three phases,
initialisation where private keys and messaging are not available (only
needed by gmem.c), initialisation without messaging but private keys
available (gslice.c, gmessage.c), and full fledged initialisers that
server the rest of glib. initialisation functions got renamed to reflect
the limitations of their corresponding phases.
* glib/gmem.c: removed memchunk code, defer allocations to
g_slice_* instead.
* glib/gmem.[hc]: removed g_slice_* skeletons.
* glib/glib.symbols: added g_slice_* symbols.
* configure.in: check for availability of posix_memalign(3), memalign(3)
and valloc(3).
* glib/Makefile.am: added gslice.[hc].
2005-12-01 Tor Lillqvist <tml@novell.com> 2005-12-01 Tor Lillqvist <tml@novell.com>
* glib/gstdio.c (g_stat): In the Win32 implementation, strip * glib/gstdio.c (g_stat): In the Win32 implementation, strip

View File

@ -1,3 +1,33 @@
Thu Dec 1 17:32:46 2005 Tim Janik <timj@imendio.com>
* glib/gslice.[hc]: new slice allocator implementation.
* tests/slice-test.c: added random slice allocation test.
* glib/gthread.[hc]: removed newly added private thread mem API.
* glib/gthreadinit.h:
* glib/gmessages.c:
* glib/gthread.c:
* glib/gmem.c: divided glib threading initialisation into three phases,
initialisation where private keys and messaging are not available (only
needed by gmem.c), initialisation without messaging but private keys
available (gslice.c, gmessage.c), and full fledged initialisers that
server the rest of glib. initialisation functions got renamed to reflect
the limitations of their corresponding phases.
* glib/gmem.c: removed memchunk code, defer allocations to
g_slice_* instead.
* glib/gmem.[hc]: removed g_slice_* skeletons.
* glib/glib.symbols: added g_slice_* symbols.
* configure.in: check for availability of posix_memalign(3), memalign(3)
and valloc(3).
* glib/Makefile.am: added gslice.[hc].
2005-12-01 Tor Lillqvist <tml@novell.com> 2005-12-01 Tor Lillqvist <tml@novell.com>
* glib/gstdio.c (g_stat): In the Win32 implementation, strip * glib/gstdio.c (g_stat): In the Win32 implementation, strip

View File

@ -523,6 +523,9 @@ AC_HEADER_STDC
AC_FUNC_VPRINTF AC_FUNC_VPRINTF
AC_FUNC_MMAP AC_FUNC_MMAP
AC_FUNC_ALLOCA AC_FUNC_ALLOCA
AC_CHECK_FUNCS(posix_memalign)
AC_CHECK_FUNCS(memalign)
AC_CHECK_FUNCS(valloc)
AC_CHECK_FUNCS(atexit on_exit) AC_CHECK_FUNCS(atexit on_exit)

View File

@ -104,6 +104,7 @@ libglib_2_0_la_SOURCES = \
grand.c \ grand.c \
gscanner.c \ gscanner.c \
gshell.c \ gshell.c \
gslice.c \
gslist.c \ gslist.c \
gstdio.c \ gstdio.c \
gstrfuncs.c \ gstrfuncs.c \
@ -179,6 +180,7 @@ glibsubinclude_HEADERS = \
grel.h \ grel.h \
gscanner.h \ gscanner.h \
gshell.h \ gshell.h \
gslice.h \
gslist.h \ gslist.h \
gspawn.h \ gspawn.h \
gstdio.h \ gstdio.h \

View File

@ -584,10 +584,6 @@ g_realloc
g_try_malloc G_GNUC_MALLOC g_try_malloc G_GNUC_MALLOC
g_try_malloc0 G_GNUC_MALLOC g_try_malloc0 G_GNUC_MALLOC
g_try_realloc g_try_realloc
g_slice_alloc
g_slice_alloc0
g_slice_free1
g_slice_free_chain
#ifndef G_DISABLE_DEPRECATED #ifndef G_DISABLE_DEPRECATED
g_allocator_free g_allocator_free
g_allocator_new g_allocator_new
@ -605,6 +601,18 @@ g_blow_chunks
#endif #endif
#endif #endif
#if IN_HEADER(__G_SLICE_H__)
#if IN_FILE(__G_SLICE_C__)
g_slice_alloc G_GNUC_MALLOC
g_slice_alloc0 G_GNUC_MALLOC
g_slice_free1
g_slice_free_chain
g_slice_set_config
g_slice_get_config
g_slice_get_config_state
#endif
#endif
#if IN_HEADER(__G_MESSAGES_H__) #if IN_HEADER(__G_MESSAGES_H__)
#if IN_FILE(__G_MESSAGES_C__) #if IN_FILE(__G_MESSAGES_C__)
g_printf_string_upper_bound g_printf_string_upper_bound

View File

@ -39,8 +39,6 @@
#include "galias.h" #include "galias.h"
/* notes on macros: /* notes on macros:
* having DISABLE_MEM_POOLS defined, disables mem_chunks alltogether, their
* allocations are performed through ordinary g_malloc/g_free.
* having G_DISABLE_CHECKS defined disables use of glib_mem_profiler_table and * having G_DISABLE_CHECKS defined disables use of glib_mem_profiler_table and
* g_mem_profile(). * g_mem_profile().
* REALLOC_0_WORKS is defined if g_realloc (NULL, x) works. * REALLOC_0_WORKS is defined if g_realloc (NULL, x) works.
@ -51,19 +49,6 @@
#define MEM_PROFILE_TABLE_SIZE 4096 #define MEM_PROFILE_TABLE_SIZE 4096
#define MEM_AREA_SIZE 4L
#ifdef G_DISABLE_CHECKS
# define ENTER_MEM_CHUNK_ROUTINE()
# define LEAVE_MEM_CHUNK_ROUTINE()
# define IN_MEM_CHUNK_ROUTINE() FALSE
#else /* !G_DISABLE_CHECKS */
static GPrivate* mem_chunk_recursion = NULL;
# define MEM_CHUNK_ROUTINE_COUNT() GPOINTER_TO_UINT (g_private_get (mem_chunk_recursion))
# define ENTER_MEM_CHUNK_ROUTINE() g_private_set (mem_chunk_recursion, GUINT_TO_POINTER (MEM_CHUNK_ROUTINE_COUNT () + 1))
# define LEAVE_MEM_CHUNK_ROUTINE() g_private_set (mem_chunk_recursion, GUINT_TO_POINTER (MEM_CHUNK_ROUTINE_COUNT () - 1))
#endif /* !G_DISABLE_CHECKS */
#ifndef REALLOC_0_WORKS #ifndef REALLOC_0_WORKS
static gpointer static gpointer
standard_realloc (gpointer mem, standard_realloc (gpointer mem,
@ -295,11 +280,9 @@ typedef enum {
} ProfilerJob; } ProfilerJob;
static guint *profile_data = NULL; static guint *profile_data = NULL;
static gulong profile_allocs = 0; static gulong profile_allocs = 0;
static gulong profile_mc_allocs = 0;
static gulong profile_zinit = 0; static gulong profile_zinit = 0;
static gulong profile_frees = 0; static gulong profile_frees = 0;
static gulong profile_mc_frees = 0; static GMutex *gmem_profile_mutex = NULL;
static GMutex *g_profile_mutex = NULL;
#ifdef G_ENABLE_DEBUG #ifdef G_ENABLE_DEBUG
static volatile gulong g_trap_free_size = 0; static volatile gulong g_trap_free_size = 0;
static volatile gulong g_trap_realloc_size = 0; static volatile gulong g_trap_realloc_size = 0;
@ -313,19 +296,17 @@ profiler_log (ProfilerJob job,
gulong n_bytes, gulong n_bytes,
gboolean success) gboolean success)
{ {
g_mutex_lock (g_profile_mutex); g_mutex_lock (gmem_profile_mutex);
if (!profile_data) if (!profile_data)
{ {
profile_data = standard_malloc ((MEM_PROFILE_TABLE_SIZE + 1) * 8 * sizeof (profile_data[0])); profile_data = standard_malloc ((MEM_PROFILE_TABLE_SIZE + 1) * 8 * sizeof (profile_data[0]));
if (!profile_data) /* memory system kiddin' me, eh? */ if (!profile_data) /* memory system kiddin' me, eh? */
{ {
g_mutex_unlock (g_profile_mutex); g_mutex_unlock (gmem_profile_mutex);
return; return;
} }
} }
if (MEM_CHUNK_ROUTINE_COUNT () == 0)
{
if (n_bytes < MEM_PROFILE_TABLE_SIZE) if (n_bytes < MEM_PROFILE_TABLE_SIZE)
profile_data[n_bytes + PROFILE_TABLE ((job & PROFILER_ALLOC) != 0, profile_data[n_bytes + PROFILE_TABLE ((job & PROFILER_ALLOC) != 0,
(job & PROFILER_RELOC) != 0, (job & PROFILER_RELOC) != 0,
@ -345,15 +326,7 @@ profiler_log (ProfilerJob job,
else else
profile_frees += n_bytes; profile_frees += n_bytes;
} }
} g_mutex_unlock (gmem_profile_mutex);
else if (success)
{
if (job & PROFILER_ALLOC)
profile_mc_allocs += n_bytes;
else
profile_mc_frees += n_bytes;
}
g_mutex_unlock (g_profile_mutex);
} }
static void static void
@ -399,27 +372,23 @@ g_mem_profile (void)
gulong local_allocs; gulong local_allocs;
gulong local_zinit; gulong local_zinit;
gulong local_frees; gulong local_frees;
gulong local_mc_allocs;
gulong local_mc_frees;
g_mutex_lock (g_profile_mutex); g_mutex_lock (gmem_profile_mutex);
local_allocs = profile_allocs; local_allocs = profile_allocs;
local_zinit = profile_zinit; local_zinit = profile_zinit;
local_frees = profile_frees; local_frees = profile_frees;
local_mc_allocs = profile_mc_allocs;
local_mc_frees = profile_mc_frees;
if (!profile_data) if (!profile_data)
{ {
g_mutex_unlock (g_profile_mutex); g_mutex_unlock (gmem_profile_mutex);
return; return;
} }
memcpy (local_data, profile_data, memcpy (local_data, profile_data,
(MEM_PROFILE_TABLE_SIZE + 1) * 8 * sizeof (profile_data[0])); (MEM_PROFILE_TABLE_SIZE + 1) * 8 * sizeof (profile_data[0]));
g_mutex_unlock (g_profile_mutex); g_mutex_unlock (gmem_profile_mutex);
g_print ("GLib Memory statistics (successful operations):\n"); g_print ("GLib Memory statistics (successful operations):\n");
profile_print_locked (local_data, TRUE); profile_print_locked (local_data, TRUE);
@ -432,11 +401,6 @@ g_mem_profile (void)
local_frees, local_frees,
((gdouble) local_frees) / local_allocs * 100.0, ((gdouble) local_frees) / local_allocs * 100.0,
local_allocs - local_frees); local_allocs - local_frees);
g_print ("MemChunk bytes: allocated=%lu, freed=%lu (%.2f%%), remaining=%lu\n",
local_mc_allocs,
local_mc_frees,
((gdouble) local_mc_frees) / local_mc_allocs * 100.0,
local_mc_allocs - local_mc_frees);
} }
static gpointer static gpointer
@ -602,47 +566,6 @@ GMemVTable *glib_mem_profiler_table = &profiler_table;
#endif /* !G_DISABLE_CHECKS */ #endif /* !G_DISABLE_CHECKS */
/* --- memory slices --- */
typedef struct {
gpointer dummy, next;
} MemSlice;
gpointer
g_slice_alloc (guint block_size)
{
return g_malloc (block_size);
}
gpointer
g_slice_alloc0 (guint block_size)
{
return g_malloc0 (block_size);
}
void
g_slice_free1 (guint block_size,
gpointer mem_block)
{
if (mem_block)
g_free (mem_block);
}
void
g_slice_free_chain (guint block_size,
gpointer mem_chain,
guint next_offset)
{
MemSlice *slice = mem_chain;
g_return_if_fail (next_offset == G_STRUCT_OFFSET (MemSlice, next));
g_return_if_fail (block_size >= sizeof (MemSlice));
while (slice)
{
MemSlice *current = slice;
slice = slice->next;
g_slice_free1 (block_size, current);
}
}
/* --- MemChunks --- */ /* --- MemChunks --- */
#ifndef G_ALLOC_AND_FREE #ifndef G_ALLOC_AND_FREE
typedef struct _GAllocator GAllocator; typedef struct _GAllocator GAllocator;
@ -651,63 +574,10 @@ typedef struct _GMemChunk GMemChunk;
#define G_ALLOC_AND_FREE 2 #define G_ALLOC_AND_FREE 2
#endif #endif
typedef struct _GFreeAtom GFreeAtom; struct _GMemChunk {
typedef struct _GMemArea GMemArea; guint alloc_size; /* the size of an atom */
struct _GFreeAtom
{
GFreeAtom *next;
}; };
struct _GMemArea
{
GMemArea *next; /* the next mem area */
GMemArea *prev; /* the previous mem area */
gulong index; /* the current index into the "mem" array */
gulong free; /* the number of free bytes in this mem area */
gulong allocated; /* the number of atoms allocated from this area */
gulong mark; /* is this mem area marked for deletion */
gchar mem[MEM_AREA_SIZE]; /* the mem array from which atoms get allocated
* the actual size of this array is determined by
* the mem chunk "area_size". ANSI says that it
* must be declared to be the maximum size it
* can possibly be (even though the actual size
* may be less).
*/
};
struct _GMemChunk
{
const gchar *name; /* name of this MemChunk...used for debugging output */
gint type; /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */
gint num_mem_areas; /* the number of memory areas */
gint num_marked_areas; /* the number of areas marked for deletion */
guint atom_size; /* the size of an atom */
gulong area_size; /* the size of a memory area */
GMemArea *mem_area; /* the current memory area */
GMemArea *mem_areas; /* a list of all the mem areas owned by this chunk */
GMemArea *free_mem_area; /* the free area...which is about to be destroyed */
GFreeAtom *free_atoms; /* the free atoms list */
GTree *mem_tree; /* tree of mem areas sorted by memory address */
GMemChunk *next; /* pointer to the next chunk */
GMemChunk *prev; /* pointer to the previous chunk */
};
#ifndef DISABLE_MEM_POOLS
static gulong g_mem_chunk_compute_size (gulong size,
gulong min_size) G_GNUC_CONST;
static gint g_mem_chunk_area_compare (GMemArea *a,
GMemArea *b);
static gint g_mem_chunk_area_search (GMemArea *a,
gchar *addr);
/* here we can't use StaticMutexes, as they depend upon a working
* g_malloc, the same holds true for StaticPrivate
*/
static GMutex *mem_chunks_lock = NULL;
static GMemChunk *mem_chunks = NULL;
GMemChunk* GMemChunk*
g_mem_chunk_new (const gchar *name, g_mem_chunk_new (const gchar *name,
gint atom_size, gint atom_size,
@ -715,537 +585,35 @@ g_mem_chunk_new (const gchar *name,
gint type) gint type)
{ {
GMemChunk *mem_chunk; GMemChunk *mem_chunk;
gulong rarea_size;
g_return_val_if_fail (atom_size > 0, NULL); g_return_val_if_fail (atom_size > 0, NULL);
g_return_val_if_fail (area_size >= atom_size, NULL);
ENTER_MEM_CHUNK_ROUTINE ();
area_size = (area_size + atom_size - 1) / atom_size;
area_size *= atom_size;
mem_chunk = g_new (GMemChunk, 1);
mem_chunk->name = name;
mem_chunk->type = type;
mem_chunk->num_mem_areas = 0;
mem_chunk->num_marked_areas = 0;
mem_chunk->mem_area = NULL;
mem_chunk->free_mem_area = NULL;
mem_chunk->free_atoms = NULL;
mem_chunk->mem_tree = NULL;
mem_chunk->mem_areas = NULL;
mem_chunk->atom_size = atom_size;
if (mem_chunk->type == G_ALLOC_AND_FREE)
mem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare);
if (mem_chunk->atom_size % G_MEM_ALIGN)
mem_chunk->atom_size += G_MEM_ALIGN - (mem_chunk->atom_size % G_MEM_ALIGN);
rarea_size = area_size + sizeof (GMemArea) - MEM_AREA_SIZE;
rarea_size = g_mem_chunk_compute_size (rarea_size, atom_size + sizeof (GMemArea) - MEM_AREA_SIZE);
mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE);
g_mutex_lock (mem_chunks_lock);
mem_chunk->next = mem_chunks;
mem_chunk->prev = NULL;
if (mem_chunks)
mem_chunks->prev = mem_chunk;
mem_chunks = mem_chunk;
g_mutex_unlock (mem_chunks_lock);
LEAVE_MEM_CHUNK_ROUTINE ();
mem_chunk = g_slice_new (GMemChunk);
mem_chunk->alloc_size = atom_size;
return mem_chunk; return mem_chunk;
} }
void void
g_mem_chunk_destroy (GMemChunk *mem_chunk) g_mem_chunk_destroy (GMemChunk *mem_chunk)
{ {
GMemArea *mem_areas;
GMemArea *temp_area;
g_return_if_fail (mem_chunk != NULL); g_return_if_fail (mem_chunk != NULL);
ENTER_MEM_CHUNK_ROUTINE (); g_slice_free (GMemChunk, mem_chunk);
mem_areas = mem_chunk->mem_areas;
while (mem_areas)
{
temp_area = mem_areas;
mem_areas = mem_areas->next;
g_free (temp_area);
}
g_mutex_lock (mem_chunks_lock);
if (mem_chunk->next)
mem_chunk->next->prev = mem_chunk->prev;
if (mem_chunk->prev)
mem_chunk->prev->next = mem_chunk->next;
if (mem_chunk == mem_chunks)
mem_chunks = mem_chunks->next;
g_mutex_unlock (mem_chunks_lock);
if (mem_chunk->type == G_ALLOC_AND_FREE)
g_tree_destroy (mem_chunk->mem_tree);
g_free (mem_chunk);
LEAVE_MEM_CHUNK_ROUTINE ();
} }
gpointer gpointer
g_mem_chunk_alloc (GMemChunk *mem_chunk) g_mem_chunk_alloc (GMemChunk *mem_chunk)
{ {
GMemArea *temp_area;
gpointer mem;
ENTER_MEM_CHUNK_ROUTINE ();
g_return_val_if_fail (mem_chunk != NULL, NULL); g_return_val_if_fail (mem_chunk != NULL, NULL);
while (mem_chunk->free_atoms) return g_slice_alloc (mem_chunk->alloc_size);
{
/* Get the first piece of memory on the "free_atoms" list.
* We can go ahead and destroy the list node we used to keep
* track of it with and to update the "free_atoms" list to
* point to its next element.
*/
mem = mem_chunk->free_atoms;
mem_chunk->free_atoms = mem_chunk->free_atoms->next;
/* Determine which area this piece of memory is allocated from */
temp_area = g_tree_search (mem_chunk->mem_tree,
(GCompareFunc) g_mem_chunk_area_search,
mem);
/* If the area has been marked, then it is being destroyed.
* (ie marked to be destroyed).
* We check to see if all of the segments on the free list that
* reference this area have been removed. This occurs when
* the ammount of free memory is less than the allocatable size.
* If the chunk should be freed, then we place it in the "free_mem_area".
* This is so we make sure not to free the mem area here and then
* allocate it again a few lines down.
* If we don't allocate a chunk a few lines down then the "free_mem_area"
* will be freed.
* If there is already a "free_mem_area" then we'll just free this mem area.
*/
if (temp_area->mark)
{
/* Update the "free" memory available in that area */
temp_area->free += mem_chunk->atom_size;
if (temp_area->free == mem_chunk->area_size)
{
if (temp_area == mem_chunk->mem_area)
mem_chunk->mem_area = NULL;
if (mem_chunk->free_mem_area)
{
mem_chunk->num_mem_areas -= 1;
if (temp_area->next)
temp_area->next->prev = temp_area->prev;
if (temp_area->prev)
temp_area->prev->next = temp_area->next;
if (temp_area == mem_chunk->mem_areas)
mem_chunk->mem_areas = mem_chunk->mem_areas->next;
if (mem_chunk->type == G_ALLOC_AND_FREE)
g_tree_remove (mem_chunk->mem_tree, temp_area);
g_free (temp_area);
}
else
mem_chunk->free_mem_area = temp_area;
mem_chunk->num_marked_areas -= 1;
}
}
else
{
/* Update the number of allocated atoms count.
*/
temp_area->allocated += 1;
/* The area wasn't marked...return the memory
*/
goto outa_here;
}
}
/* If there isn't a current mem area or the current mem area is out of space
* then allocate a new mem area. We'll first check and see if we can use
* the "free_mem_area". Otherwise we'll just malloc the mem area.
*/
if ((!mem_chunk->mem_area) ||
((mem_chunk->mem_area->index + mem_chunk->atom_size) > mem_chunk->area_size))
{
if (mem_chunk->free_mem_area)
{
mem_chunk->mem_area = mem_chunk->free_mem_area;
mem_chunk->free_mem_area = NULL;
}
else
{
#ifdef ENABLE_GC_FRIENDLY
mem_chunk->mem_area = (GMemArea*) g_malloc0 (sizeof (GMemArea) -
MEM_AREA_SIZE +
mem_chunk->area_size);
#else /* !ENABLE_GC_FRIENDLY */
mem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) -
MEM_AREA_SIZE +
mem_chunk->area_size);
#endif /* ENABLE_GC_FRIENDLY */
mem_chunk->num_mem_areas += 1;
mem_chunk->mem_area->next = mem_chunk->mem_areas;
mem_chunk->mem_area->prev = NULL;
if (mem_chunk->mem_areas)
mem_chunk->mem_areas->prev = mem_chunk->mem_area;
mem_chunk->mem_areas = mem_chunk->mem_area;
if (mem_chunk->type == G_ALLOC_AND_FREE)
g_tree_insert (mem_chunk->mem_tree, mem_chunk->mem_area, mem_chunk->mem_area);
}
mem_chunk->mem_area->index = 0;
mem_chunk->mem_area->free = mem_chunk->area_size;
mem_chunk->mem_area->allocated = 0;
mem_chunk->mem_area->mark = 0;
}
/* Get the memory and modify the state variables appropriately.
*/
mem = (gpointer) &mem_chunk->mem_area->mem[mem_chunk->mem_area->index];
mem_chunk->mem_area->index += mem_chunk->atom_size;
mem_chunk->mem_area->free -= mem_chunk->atom_size;
mem_chunk->mem_area->allocated += 1;
outa_here:
LEAVE_MEM_CHUNK_ROUTINE ();
return mem;
} }
gpointer gpointer
g_mem_chunk_alloc0 (GMemChunk *mem_chunk) g_mem_chunk_alloc0 (GMemChunk *mem_chunk)
{ {
gpointer mem;
mem = g_mem_chunk_alloc (mem_chunk);
if (mem)
{
memset (mem, 0, mem_chunk->atom_size);
}
return mem;
}
void
g_mem_chunk_free (GMemChunk *mem_chunk,
gpointer mem)
{
GMemArea *temp_area;
GFreeAtom *free_atom;
g_return_if_fail (mem_chunk != NULL);
g_return_if_fail (mem != NULL);
ENTER_MEM_CHUNK_ROUTINE ();
#ifdef ENABLE_GC_FRIENDLY
memset (mem, 0, mem_chunk->atom_size);
#endif /* ENABLE_GC_FRIENDLY */
/* Don't do anything if this is an ALLOC_ONLY chunk
*/
if (mem_chunk->type == G_ALLOC_AND_FREE)
{
/* Place the memory on the "free_atoms" list
*/
free_atom = (GFreeAtom*) mem;
free_atom->next = mem_chunk->free_atoms;
mem_chunk->free_atoms = free_atom;
temp_area = g_tree_search (mem_chunk->mem_tree,
(GCompareFunc) g_mem_chunk_area_search,
mem);
temp_area->allocated -= 1;
if (temp_area->allocated == 0)
{
temp_area->mark = 1;
mem_chunk->num_marked_areas += 1;
}
}
LEAVE_MEM_CHUNK_ROUTINE ();
}
/* This doesn't free the free_area if there is one */
void
g_mem_chunk_clean (GMemChunk *mem_chunk)
{
GMemArea *mem_area;
GFreeAtom *prev_free_atom;
GFreeAtom *temp_free_atom;
gpointer mem;
g_return_if_fail (mem_chunk != NULL);
ENTER_MEM_CHUNK_ROUTINE ();
if (mem_chunk->type == G_ALLOC_AND_FREE)
{
prev_free_atom = NULL;
temp_free_atom = mem_chunk->free_atoms;
while (temp_free_atom)
{
mem = (gpointer) temp_free_atom;
mem_area = g_tree_search (mem_chunk->mem_tree,
(GCompareFunc) g_mem_chunk_area_search,
mem);
/* If this mem area is marked for destruction then delete the
* area and list node and decrement the free mem.
*/
if (mem_area->mark)
{
if (prev_free_atom)
prev_free_atom->next = temp_free_atom->next;
else
mem_chunk->free_atoms = temp_free_atom->next;
temp_free_atom = temp_free_atom->next;
mem_area->free += mem_chunk->atom_size;
if (mem_area->free == mem_chunk->area_size)
{
mem_chunk->num_mem_areas -= 1;
mem_chunk->num_marked_areas -= 1;
if (mem_area->next)
mem_area->next->prev = mem_area->prev;
if (mem_area->prev)
mem_area->prev->next = mem_area->next;
if (mem_area == mem_chunk->mem_areas)
mem_chunk->mem_areas = mem_chunk->mem_areas->next;
if (mem_area == mem_chunk->mem_area)
mem_chunk->mem_area = NULL;
if (mem_chunk->type == G_ALLOC_AND_FREE)
g_tree_remove (mem_chunk->mem_tree, mem_area);
g_free (mem_area);
}
}
else
{
prev_free_atom = temp_free_atom;
temp_free_atom = temp_free_atom->next;
}
}
}
LEAVE_MEM_CHUNK_ROUTINE ();
}
void
g_mem_chunk_reset (GMemChunk *mem_chunk)
{
GMemArea *mem_areas;
GMemArea *temp_area;
g_return_if_fail (mem_chunk != NULL);
ENTER_MEM_CHUNK_ROUTINE ();
mem_areas = mem_chunk->mem_areas;
mem_chunk->num_mem_areas = 0;
mem_chunk->mem_areas = NULL;
mem_chunk->mem_area = NULL;
while (mem_areas)
{
temp_area = mem_areas;
mem_areas = mem_areas->next;
g_free (temp_area);
}
mem_chunk->free_atoms = NULL;
if (mem_chunk->mem_tree)
{
g_tree_destroy (mem_chunk->mem_tree);
mem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare);
}
LEAVE_MEM_CHUNK_ROUTINE ();
}
void
g_mem_chunk_print (GMemChunk *mem_chunk)
{
GMemArea *mem_areas;
gulong mem;
g_return_if_fail (mem_chunk != NULL);
mem_areas = mem_chunk->mem_areas;
mem = 0;
while (mem_areas)
{
mem += mem_chunk->area_size - mem_areas->free;
mem_areas = mem_areas->next;
}
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO,
"%s: %ld bytes using %d mem areas",
mem_chunk->name, mem, mem_chunk->num_mem_areas);
}
void
g_mem_chunk_info (void)
{
GMemChunk *mem_chunk;
gint count;
count = 0;
g_mutex_lock (mem_chunks_lock);
mem_chunk = mem_chunks;
while (mem_chunk)
{
count += 1;
mem_chunk = mem_chunk->next;
}
g_mutex_unlock (mem_chunks_lock);
g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "%d mem chunks", count);
g_mutex_lock (mem_chunks_lock);
mem_chunk = mem_chunks;
g_mutex_unlock (mem_chunks_lock);
while (mem_chunk)
{
g_mem_chunk_print ((GMemChunk*) mem_chunk);
mem_chunk = mem_chunk->next;
}
}
void
g_blow_chunks (void)
{
GMemChunk *mem_chunk;
g_mutex_lock (mem_chunks_lock);
mem_chunk = mem_chunks;
g_mutex_unlock (mem_chunks_lock);
while (mem_chunk)
{
g_mem_chunk_clean ((GMemChunk*) mem_chunk);
mem_chunk = mem_chunk->next;
}
}
static gulong
g_mem_chunk_compute_size (gulong size,
gulong min_size)
{
gulong power_of_2;
gulong lower, upper;
power_of_2 = 16;
while (power_of_2 < size)
power_of_2 <<= 1;
lower = power_of_2 >> 1;
upper = power_of_2;
if (size - lower < upper - size && lower >= min_size)
return lower;
else
return upper;
}
static gint
g_mem_chunk_area_compare (GMemArea *a,
GMemArea *b)
{
if (a->mem > b->mem)
return 1;
else if (a->mem < b->mem)
return -1;
return 0;
}
static gint
g_mem_chunk_area_search (GMemArea *a,
gchar *addr)
{
if (a->mem <= addr)
{
if (addr < &a->mem[a->index])
return 0;
return 1;
}
return -1;
}
#else /* DISABLE_MEM_POOLS */
typedef struct {
guint alloc_size; /* the size of an atom */
} GMinimalMemChunk;
GMemChunk*
g_mem_chunk_new (const gchar *name,
gint atom_size,
gulong area_size,
gint type)
{
GMinimalMemChunk *mem_chunk;
g_return_val_if_fail (atom_size > 0, NULL);
mem_chunk = g_new (GMinimalMemChunk, 1);
mem_chunk->alloc_size = atom_size;
return ((GMemChunk*) mem_chunk);
}
void
g_mem_chunk_destroy (GMemChunk *mem_chunk)
{
g_return_if_fail (mem_chunk != NULL);
g_free (mem_chunk);
}
gpointer
g_mem_chunk_alloc (GMemChunk *mem_chunk)
{
GMinimalMemChunk *minimal = (GMinimalMemChunk *)mem_chunk;
g_return_val_if_fail (mem_chunk != NULL, NULL); g_return_val_if_fail (mem_chunk != NULL, NULL);
return g_malloc (minimal->alloc_size); return g_slice_alloc0 (mem_chunk->alloc_size);
}
gpointer
g_mem_chunk_alloc0 (GMemChunk *mem_chunk)
{
GMinimalMemChunk *minimal = (GMinimalMemChunk *)mem_chunk;
g_return_val_if_fail (mem_chunk != NULL, NULL);
return g_malloc0 (minimal->alloc_size);
} }
void void
@ -1254,7 +622,7 @@ g_mem_chunk_free (GMemChunk *mem_chunk,
{ {
g_return_if_fail (mem_chunk != NULL); g_return_if_fail (mem_chunk != NULL);
g_free (mem); g_slice_free1 (mem_chunk->alloc_size, mem);
} }
void g_mem_chunk_clean (GMemChunk *mem_chunk) {} void g_mem_chunk_clean (GMemChunk *mem_chunk) {}
@ -1263,10 +631,11 @@ void g_mem_chunk_print (GMemChunk *mem_chunk) {}
void g_mem_chunk_info (void) {} void g_mem_chunk_info (void) {}
void g_blow_chunks (void) {} void g_blow_chunks (void) {}
#endif /* DISABLE_MEM_POOLS */ GAllocator*
g_allocator_new (const gchar *name,
struct _GAllocator guint n_preallocs)
{ {
static const struct _GAllocator {
gchar *name; gchar *name;
guint16 n_preallocs; guint16 n_preallocs;
guint is_unused : 1; guint is_unused : 1;
@ -1274,17 +643,11 @@ struct _GAllocator
GAllocator *last; GAllocator *last;
GMemChunk *mem_chunk; GMemChunk *mem_chunk;
gpointer free_list; gpointer free_list;
}; } dummy = {
GAllocator*
g_allocator_new (const gchar *name,
guint n_preallocs)
{
static const GAllocator dummy = {
"GAllocator is deprecated", 1, TRUE, 0, NULL, NULL, NULL, "GAllocator is deprecated", 1, TRUE, 0, NULL, NULL, NULL,
}; };
/* some (broken) GAllocator uses depend on non-NULL allocators */ /* some (broken) GAllocator uses depend on non-NULL allocators */
return (GAllocator*) &dummy; return (void*) &dummy;
} }
void void
@ -1293,22 +656,13 @@ g_allocator_free (GAllocator *allocator)
} }
void void
_g_mem_thread_init (void) _g_mem_thread_init_noprivate_nomessage (void)
{ {
#ifndef DISABLE_MEM_POOLS /* we may only create mutexes here, locking/unlocking itself
mem_chunks_lock = g_mutex_new (); * does not yet work.
#endif */
#ifndef G_DISABLE_CHECKS #ifndef G_DISABLE_CHECKS
g_profile_mutex = g_mutex_new (); gmem_profile_mutex = g_mutex_new ();
#endif
}
void
_g_mem_thread_private_init (void)
{
#ifndef G_DISABLE_CHECKS
g_assert (mem_chunk_recursion == NULL);
mem_chunk_recursion = g_private_new (NULL);
#endif #endif
} }

View File

@ -27,6 +27,7 @@
#ifndef __G_MEM_H__ #ifndef __G_MEM_H__
#define __G_MEM_H__ #define __G_MEM_H__
#include <glib/gslice.h>
#include <glib/gtypes.h> #include <glib/gtypes.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -96,19 +97,6 @@ gboolean g_mem_is_system_malloc (void);
GLIB_VAR GMemVTable *glib_mem_profiler_table; GLIB_VAR GMemVTable *glib_mem_profiler_table;
void g_mem_profile (void); void g_mem_profile (void);
/* slices - fast allocation/release of small memory blocks
*/
gpointer g_slice_alloc (guint block_size);
gpointer g_slice_alloc0 (guint block_size);
void g_slice_free1 (guint block_size,
gpointer mem_block);
void g_slice_free_chain (guint block_size,
gpointer mem_chain,
guint next_offset);
#define g_slice_new(type) ((type*) g_slice_alloc (sizeof (type)))
#define g_slice_new0(type) ((type*) g_slice_alloc0 (sizeof (type)))
#define g_slice_free(type,mem) g_slice_free1 (sizeof (type), mem)
/* deprecated memchunks and allocators */ /* deprecated memchunks and allocators */
#if !defined G_DISABLE_DEPRECATED || 1 #if !defined G_DISABLE_DEPRECATED || 1

View File

@ -1041,20 +1041,14 @@ g_printf_string_upper_bound (const gchar *format,
} }
void void
_g_messages_thread_init (void) _g_messages_thread_init_nomessage (void)
{ {
g_messages_lock = g_mutex_new (); g_messages_lock = g_mutex_new ();
g_log_depth = g_private_new (NULL);
g_messages_prefixed_init (); g_messages_prefixed_init ();
_g_debug_init (); _g_debug_init ();
} }
void
_g_messages_thread_private_init (void)
{
g_assert (g_log_depth == NULL);
g_log_depth = g_private_new (NULL);
}
gboolean _g_debug_initialized = FALSE; gboolean _g_debug_initialized = FALSE;
guint _g_debug_flags = 0; guint _g_debug_flags = 0;

983
glib/gslice.c Normal file
View File

@ -0,0 +1,983 @@
/* GLIB sliced memory - fast concurrent memory chunk allocator
* Copyright (C) 2005 Tim Janik
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/* MT safe */
#define _XOPEN_SOURCE 600 /* posix_memalign() */
#include <stdlib.h> /* posix_memalign() */
#include <unistd.h> /* sysconf() */
#include <assert.h> /* assert() for nomessage phase */
#include <string.h>
#include <errno.h>
#include "config.h"
#include "gmem.h" /* gslice.h */
#include "gthreadinit.h"
#include "galias.h"
#include "glib.h"
/* the GSlice allocator is split up into 4 layers, roughly modelled after the slab
* allocator and magazine extensions as outlined in:
* + [Bonwick94] Jeff Bonwick, The slab allocator: An object-caching kernel
* memory allocator. USENIX 1994, http://citeseer.ist.psu.edu/bonwick94slab.html
* + [Bonwick01] Bonwick and Jonathan Adams, Magazines and vmem: Extending the
* slab allocator to many cpu's and arbitrary resources.
* USENIX 2001, http://citeseer.ist.psu.edu/bonwick01magazines.html
* the layers are:
* - the thread magazines. for each (aligned) chunk size, a magazine (a list)
* of recently freed and soon to be allocated chunks is maintained per thread.
* this way, most alloc/free requests can be quickly satisfied from per-thread
* free lists which only require one g_private_get() call to retrive the
* thread handle.
* - the magazine cache. allocating and freeing chunks to/from threads only
* occours at magazine sizes from a global depot of magazines. the depot
* maintaines a 15 second working set of allocated magazines, so full
* magazines are not allocated and released too often.
* the chunk size dependent magazine sizes automatically adapt (within limits,
* see [3]) to lock contention to properly scale performance across a variety
* of SMP systems.
* - the slab allocator. this allocator allocates slabs (blocks of memory) close
* to the system page size or multiples thereof which have to be page aligned.
* the blocks are divided into smaller chunks which are used to satisfy
* allocations from the upper layers. the space provided by the reminder of
* the chunk size division is used for cache colorization (random distribution
* of chunk addresses) to improve processor cache utilization. multiple slabs
* with the same chunk size are kept in a partially sorted ring to allow O(1)
* freeing and allocation of chunks (as long as the allocation of an entirely
* new slab can be avoided).
* - the page allocator. on most modern systems, posix_memalign(3) or
* memalign(3) should be available, so this is used to allocate blocks with
* system page size based alignments and sizes or multiples thereof.
* if no memalign variant is provided, valloc() is used instead and
* block sizes are limited to the system page size (no multiples thereof).
* as a fallback, on system without even valloc(), a malloc(3)-based page
* allocator with alloc-only behaviour is used.
*
* NOTES:
* [1] some systems memalign(3) implementations may rely on boundary tagging for
* the handed out memory chunks. to avoid excessive page-wise fragmentation,
* we reserve 2 * sizeof (void*) per block size for the systems memalign(3),
* specified in NATIVE_MALLOC_PADDING.
* [2] using the slab allocator alone already provides for a fast and efficient
* allocator, it doesn't properly scale beyond single-threaded uses though.
* also, the slab allocator implements eager free(3)-ing, i.e. does not
* provide any form of caching or working set maintenance. so if used alone,
* it's vulnerable to trashing for sequences of balanced (alloc, free) pairs
* at certain thresholds.
* [3] magazine sizes are bound by an implementation specific minimum size and
* a chunk size specific maximum to limit magazine storage sizes to roughly
* 16KB.
* [4] allocating ca. 8 chunks per block/page keeps a good balance between
* external and internal fragmentation (<= 12.5%) [Bonwick94]
*/
/* --- macros and constants --- */
#define LARGEALIGNMENT (256)
#define P2ALIGNMENT (2 * sizeof (gsize)) /* fits 2 pointers (assumed to be 2 * GLIB_SIZEOF_SIZE_T below) */
#define ALIGN(size, base) ((base) * (gsize) (((size) + (base) - 1) / (base)))
#define NATIVE_MALLOC_PADDING P2ALIGNMENT /* per-page padding left for native malloc(3) see [1] */
#define SLAB_INFO_SIZE P2ALIGN (sizeof (SlabInfo) + NATIVE_MALLOC_PADDING)
#define MAX_MAGAZINE_SIZE (256) /* see [3] and allocator_get_magazine_threshold() for this */
#define MIN_MAGAZINE_SIZE (4)
#define MAX_STAMP_COUNTER (13) /* distributes the load of gettimeofday() */
#define MAX_SLAB_CHUNK_SIZE(al) (((al)->max_page_size - SLAB_INFO_SIZE) / 8) /* we want at last 8 chunks per page, see [4] */
#define MAX_SLAB_INDEX(al) (SLAB_INDEX (al, MAX_SLAB_CHUNK_SIZE (al)) + 1)
#define SLAB_INDEX(al, asize) ((asize) / P2ALIGNMENT - 1) /* asize must be P2ALIGNMENT aligned */
#define SLAB_CHUNK_SIZE(al, ix) (((ix) + 1) * P2ALIGNMENT)
#define SLAB_PAGE_SIZE(al,csz) (ALIGN (8 * (csz) + SLAB_INFO_SIZE, (al)->min_page_size))
/* optimized version of ALIGN (size, P2ALIGNMENT) */
#if GLIB_SIZEOF_SIZE_T * 2 == 8 /* P2ALIGNMENT */
#define P2ALIGN(size) (((size) + 0x7) & ~(gsize) 0x7)
#elif GLIB_SIZEOF_SIZE_T * 2 == 16 /* P2ALIGNMENT */
#define P2ALIGN(size) (((size) + 0xf) & ~(gsize) 0xf)
#else
#define P2ALIGN(size) ALIGN (size, P2ALIGNMENT)
#endif
/* --- structures --- */
typedef struct _ChunkLink ChunkLink;
typedef struct _SlabInfo SlabInfo;
typedef struct _CachedMagazine CachedMagazine;
struct _ChunkLink {
ChunkLink *next;
ChunkLink *data;
};
struct _SlabInfo {
ChunkLink *chunks;
guint n_allocated;
SlabInfo *next, *prev;
};
typedef struct {
ChunkLink *chunks;
gsize count; /* approximative chunks list length */
} Magazine;
typedef struct {
Magazine *magazine1; /* array of MAX_SLAB_INDEX (allocator) */
Magazine *magazine2; /* array of MAX_SLAB_INDEX (allocator) */
} ThreadMemory;
typedef struct {
gboolean always_malloc;
gboolean bypass_magazines;
gboolean always_free;
gsize working_set_msecs;
} SliceConfig;
typedef struct {
/* const after initialization */
gsize min_page_size, max_page_size;
SliceConfig config;
guint max_slab_chunk_size_for_magazine_cache;
/* magazine cache */
GMutex *magazine_mutex;
ChunkLink **magazines; /* array of MAX_SLAB_INDEX (allocator) */
guint *contention_counters; /* array of MAX_SLAB_INDEX (allocator) */
gint mutex_counter;
guint stamp_counter;
guint last_stamp;
/* slab allocator */
GMutex *slab_mutex;
SlabInfo **slab_stack; /* array of MAX_SLAB_INDEX (allocator) */
guint color_accu;
} Allocator;
/* --- prototypes --- */
static gpointer slab_allocator_alloc_chunk (guint chunk_size);
static void slab_allocator_free_chunk (guint chunk_size,
gpointer mem);
static void private_thread_memory_cleanup (gpointer data);
static gpointer allocator_memalign (gsize alignment,
gsize memsize);
static void allocator_memfree (gsize memsize,
gpointer mem);
static inline void magazine_cache_update_stamp (void);
static inline guint allocator_get_magazine_threshold (Allocator *allocator,
guint ix);
/* --- variables --- */
static GPrivate *private_thread_memory = NULL;
static gsize sys_page_size = 0;
static Allocator allocator[1] = { { 0, }, };
static SliceConfig slice_config = {
FALSE, /* always_malloc */
FALSE, /* bypass_magazines */
FALSE, /* always_free */
15 * 1000, /* working_set_msecs */
};
/* --- auxillary funcitons --- */
void
g_slice_set_config (GSliceConfig ckey,
gint64 value)
{
g_return_if_fail (sys_page_size == 0);
switch (ckey)
{
case G_SLICE_CONFIG_ALWAYS_MALLOC:
slice_config.always_malloc = value != 0;
break;
case G_SLICE_CONFIG_BYPASS_MAGAZINES:
slice_config.bypass_magazines = value != 0;
break;
case G_SLICE_CONFIG_ALWAYS_FREE:
slice_config.always_free = value != 0;
break;
case G_SLICE_CONFIG_WORKING_SET_MSECS:
slice_config.working_set_msecs = value;
break;
default: ;
}
}
gint64
g_slice_get_config (GSliceConfig ckey)
{
switch (ckey)
{
case G_SLICE_CONFIG_ALWAYS_MALLOC:
return slice_config.always_malloc;
case G_SLICE_CONFIG_BYPASS_MAGAZINES:
return slice_config.bypass_magazines;
case G_SLICE_CONFIG_ALWAYS_FREE:
return slice_config.always_free;
case G_SLICE_CONFIG_WORKING_SET_MSECS:
return slice_config.working_set_msecs;
case G_SLICE_CONFIG_CHUNK_SIZES:
return MAX_SLAB_INDEX (allocator);
default:
return 0;
}
}
gint64*
g_slice_get_config_state (GSliceConfig ckey,
gint64 address,
guint *n_values)
{
guint i = 0;
g_return_val_if_fail (n_values != NULL, NULL);
*n_values = 0;
switch (ckey)
{
gint64 array[64];
case G_SLICE_CONFIG_CONTENTION_COUNTER:
array[i++] = SLAB_CHUNK_SIZE (allocator, address);
array[i++] = allocator->contention_counters[address];
array[i++] = allocator_get_magazine_threshold (allocator, address);
*n_values = i;
return g_memdup (array, sizeof (array[0]) * *n_values);
default:
return NULL;
}
}
static void
g_slice_init_nomessage (void)
{
/* we may not use g_error() or friends here */
assert (sys_page_size == 0);
sys_page_size = sysconf (_SC_PAGESIZE); /* = sysconf (_SC_PAGE_SIZE); = getpagesize(); */
assert (sys_page_size >= 2 * LARGEALIGNMENT);
allocator->config = slice_config;
allocator->min_page_size = sys_page_size;
#if HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN
/* allow allocation of pages up to 8KB (with 8KB alignment).
* this is useful because many medium to large sized structures
* fit less than 8 times (see [4]) into 4KB pages.
*/
allocator->min_page_size = MAX (allocator->min_page_size, 4096);
allocator->max_page_size = MAX (allocator->min_page_size, 8192);
#else
/* we can only align to system page size */
allocator->max_page_size = sys_page_size;
#endif
allocator->magazine_mutex = NULL; /* _g_slice_thread_init_nomessage() */
allocator->magazines = g_new0 (ChunkLink*, MAX_SLAB_INDEX (allocator));
allocator->contention_counters = g_new0 (guint, MAX_SLAB_INDEX (allocator));
allocator->mutex_counter = 0;
allocator->stamp_counter = MAX_STAMP_COUNTER; /* force initial update */
allocator->last_stamp = 0;
allocator->slab_mutex = NULL; /* _g_slice_thread_init_nomessage() */
allocator->slab_stack = g_new0 (SlabInfo*, MAX_SLAB_INDEX (allocator));
allocator->color_accu = 0;
magazine_cache_update_stamp();
/* values cached for performance reasons */
allocator->max_slab_chunk_size_for_magazine_cache = MAX_SLAB_CHUNK_SIZE (allocator);
if (allocator->config.always_malloc || allocator->config.bypass_magazines)
allocator->max_slab_chunk_size_for_magazine_cache = 0; /* non-optimized cases */
}
static inline guint
allocator_categorize (guint aligned_chunk_size)
{
/* speed up the likely path */
if (G_LIKELY (aligned_chunk_size && aligned_chunk_size <= allocator->max_slab_chunk_size_for_magazine_cache))
return 1; /* use magazine cache */
/* the above will fail (max_slab_chunk_size_for_magazine_cache == 0) if the
* allocator is still uninitialized, or if we are not configured to use the
* magazine cache.
*/
if (!sys_page_size)
g_slice_init_nomessage ();
if (!allocator->config.always_malloc &&
aligned_chunk_size &&
aligned_chunk_size <= MAX_SLAB_CHUNK_SIZE (allocator))
{
if (allocator->config.bypass_magazines)
return 2; /* use slab allocator, see [2] */
return 1; /* use magazine cache */
}
return 0; /* use malloc() */
}
void
_g_slice_thread_init_nomessage (void)
{
/* we may not use g_error() or friends here */
if (!sys_page_size)
g_slice_init_nomessage();
private_thread_memory = g_private_new (private_thread_memory_cleanup);
allocator->magazine_mutex = g_mutex_new();
allocator->slab_mutex = g_mutex_new();
}
static inline void
g_mutex_lock_a (GMutex *mutex,
guint *threshold)
{
gboolean contention = FALSE;
if (!g_mutex_trylock (mutex))
{
g_mutex_lock (mutex);
contention = TRUE;
}
if (contention)
{
allocator->mutex_counter++;
if (allocator->mutex_counter >= 1) /* quickly adapt to contention */
{
allocator->mutex_counter = 0;
*threshold = MIN (*threshold + 1, MAX_MAGAZINE_SIZE);
}
}
else /* !contention */
{
allocator->mutex_counter--;
if (allocator->mutex_counter < -11) /* moderately recover magazine sizes */
{
allocator->mutex_counter = 0;
*threshold = MAX (*threshold, 1) - 1;
}
}
}
static inline ThreadMemory*
thread_memory_from_self (void)
{
ThreadMemory *tmem = g_private_get (private_thread_memory);
if (G_UNLIKELY (!tmem))
{
const guint n_magazines = MAX_SLAB_INDEX (allocator);
tmem = g_malloc0 (sizeof (ThreadMemory) + sizeof (Magazine) * 2 * n_magazines);
tmem->magazine1 = (Magazine*) (tmem + 1);
tmem->magazine2 = &tmem->magazine1[n_magazines];
g_private_set (private_thread_memory, tmem);
}
return tmem;
}
static inline ChunkLink*
magazine_chain_pop_head (ChunkLink **magazine_chunks)
{
/* magazine chains are linked via ChunkLink->next.
* each ChunkLink->data of the toplevel chain may point to a subchain,
* linked via ChunkLink->next. ChunkLink->data of the subchains just
* contains uninitialized junk.
*/
ChunkLink *chunk = (*magazine_chunks)->data;
if (G_UNLIKELY (chunk))
{
/* allocating from freed list */
(*magazine_chunks)->data = chunk->next;
}
else
{
chunk = *magazine_chunks;
*magazine_chunks = chunk->next;
}
return chunk;
}
static guint
magazine_count (ChunkLink *head)
{
guint count = 0;
if (!head)
return 0;
while (head)
{
ChunkLink *child = head->data;
count += 1;
for (child = head->data; child; child = child->next)
count += 1;
head = head->next;
}
return count;
}
static inline guint
allocator_get_magazine_threshold (Allocator *allocator,
guint ix)
{
/* the magazine size calculated here has a lower bound of MIN_MAGAZINE_SIZE,
* which is required by the implementation. also, for moderately sized chunks
* (say >= 64 bytes), magazine sizes shouldn't be much smaller then the number
* of chunks available per page to avoid excessive traffic in the magazine
* cache for small to medium sized structures.
* the upper bound of the magazine size is effectively provided by
* MAX_MAGAZINE_SIZE. for larger chunks, this number is scaled down so that
* the content of a single magazine doesn't exceed ca. 16KB.
*/
guint chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
guint threshold = MAX (MIN_MAGAZINE_SIZE, sys_page_size / MAX (chunk_size, 64));
guint contention_counter = allocator->contention_counters[ix];
if (G_UNLIKELY (contention_counter)) /* single CPU bias */
{
/* adapt contention counter thresholds to chunk sizes */
contention_counter = contention_counter * 64 / chunk_size;
threshold = MAX (threshold, contention_counter);
}
return threshold;
}
/* --- magazine cache --- */
static inline void
magazine_cache_update_stamp (void)
{
if (allocator->stamp_counter >= MAX_STAMP_COUNTER)
{
GTimeVal tv;
g_get_current_time (&tv);
allocator->last_stamp = tv.tv_sec * 1000 + tv.tv_usec / 1000; /* milli seconds */
allocator->stamp_counter = 0;
}
else
allocator->stamp_counter++;
}
static inline ChunkLink*
magazine_chain_prepare_fields (ChunkLink *magazine_chunks)
{
g_assert (MIN_MAGAZINE_SIZE >= 4);
/* ensure a magazine with at least 4 unused data pointers */
ChunkLink *chunk1 = magazine_chain_pop_head (&magazine_chunks);
ChunkLink *chunk2 = magazine_chain_pop_head (&magazine_chunks);
ChunkLink *chunk3 = magazine_chain_pop_head (&magazine_chunks);
ChunkLink *chunk4 = magazine_chain_pop_head (&magazine_chunks);
chunk4->next = magazine_chunks;
chunk3->next = chunk4;
chunk2->next = chunk3;
chunk1->next = chunk2;
return chunk1;
}
/* access the first 3 fields of a specially prepared magazine chain */
#define magazine_chain_prev(mc) ((mc)->data)
#define magazine_chain_stamp(mc) ((mc)->next->data)
#define magazine_chain_next(mc) ((mc)->next->next->data)
#define magazine_chain_count(mc) ((mc)->next->next->next->data)
static void
magazine_cache_trim (Allocator *allocator,
guint ix,
guint stamp)
{
/* g_mutex_lock (allocator->mutex); done by caller */
/* trim magazine cache from tail */
ChunkLink *current = magazine_chain_prev (allocator->magazines[ix]);
ChunkLink *trash = NULL;
while (allocator->config.always_free ||
ABS (stamp - (guint) magazine_chain_stamp (current)) > allocator->config.working_set_msecs)
{
/* unlink */
ChunkLink *prev = magazine_chain_prev (current);
ChunkLink *next = magazine_chain_next (current);
magazine_chain_next (prev) = next;
magazine_chain_prev (next) = prev;
/* clear special fields, put on trash stack */
magazine_chain_next (current) = NULL;
magazine_chain_count (current) = NULL;
magazine_chain_stamp (current) = NULL;
magazine_chain_prev (current) = trash;
trash = current;
/* fixup list head if required */
if (current == allocator->magazines[ix])
{
allocator->magazines[ix] = NULL;
break;
}
current = prev;
}
g_mutex_unlock (allocator->magazine_mutex);
/* free trash */
if (trash)
{
const guint chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
g_mutex_lock (allocator->slab_mutex);
while (trash)
{
current = trash;
trash = magazine_chain_prev (current);
magazine_chain_prev (current) = NULL; /* clear special field */
while (current)
{
ChunkLink *chunk = magazine_chain_pop_head (&current);
slab_allocator_free_chunk (chunk_size, chunk);
}
}
g_mutex_unlock (allocator->slab_mutex);
}
}
static void
magazine_cache_push_magazine (guint ix,
ChunkLink *magazine_chunks,
gsize count) /* must be >= MIN_MAGAZINE_SIZE */
{
ChunkLink *current = magazine_chain_prepare_fields (magazine_chunks);
ChunkLink *next, *prev;
g_mutex_lock (allocator->magazine_mutex);
/* add magazine at head */
next = allocator->magazines[ix];
if (next)
prev = magazine_chain_prev (next);
else
next = prev = current;
magazine_chain_next (prev) = current;
magazine_chain_prev (next) = current;
magazine_chain_prev (current) = prev;
magazine_chain_next (current) = next;
magazine_chain_count (current) = (gpointer) count;
/* stamp magazine */
magazine_cache_update_stamp();
magazine_chain_stamp (current) = (gpointer) allocator->last_stamp;
allocator->magazines[ix] = current;
/* free old magazines beyond a certain threshold */
magazine_cache_trim (allocator, ix, allocator->last_stamp);
/* g_mutex_unlock (allocator->mutex); was done by magazine_cache_trim() */
}
static ChunkLink*
magazine_cache_pop_magazine (guint ix,
gsize *countp)
{
g_mutex_lock_a (allocator->magazine_mutex, &allocator->contention_counters[ix]);
if (!allocator->magazines[ix])
{
guint magazine_threshold = allocator_get_magazine_threshold (allocator, ix);
gsize i, chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
ChunkLink *current = NULL;
g_mutex_unlock (allocator->magazine_mutex);
g_mutex_lock (allocator->slab_mutex);
for (i = 0; i < magazine_threshold; i++)
{
ChunkLink *chunk = slab_allocator_alloc_chunk (chunk_size);
chunk->data = NULL;
chunk->next = current;
current = chunk;
}
g_mutex_unlock (allocator->slab_mutex);
*countp = i;
return current;
}
else
{
ChunkLink *current = allocator->magazines[ix];
ChunkLink *prev = magazine_chain_prev (current);
ChunkLink *next = magazine_chain_next (current);
/* unlink */
magazine_chain_next (prev) = next;
magazine_chain_prev (next) = prev;
allocator->magazines[ix] = next == current ? NULL : next;
g_mutex_unlock (allocator->magazine_mutex);
/* clear special fields and hand out */
*countp = (gsize) magazine_chain_count (current);
magazine_chain_prev (current) = NULL;
magazine_chain_next (current) = NULL;
magazine_chain_count (current) = NULL;
magazine_chain_stamp (current) = NULL;
return current;
}
}
/* --- thread magazines --- */
static void
private_thread_memory_cleanup (gpointer data)
{
ThreadMemory *tmem = data;
const guint n_magazines = MAX_SLAB_INDEX (allocator);
guint ix;
for (ix = 0; ix < n_magazines; ix++)
{
Magazine *mags[2];
guint j;
mags[0] = &tmem->magazine1[ix];
mags[1] = &tmem->magazine2[ix];
for (j = 0; j < 2; j++)
{
Magazine *mag = mags[j];
if (mag->count >= MIN_MAGAZINE_SIZE)
magazine_cache_push_magazine (ix, mag->chunks, mag->count);
else
{
const guint chunk_size = SLAB_CHUNK_SIZE (allocator, ix);
g_mutex_lock (allocator->slab_mutex);
while (mag->chunks)
{
ChunkLink *chunk = magazine_chain_pop_head (&mag->chunks);
slab_allocator_free_chunk (chunk_size, chunk);
}
g_mutex_unlock (allocator->slab_mutex);
}
}
}
g_free (tmem);
}
static void
thread_memory_magazine1_reload (ThreadMemory *tmem,
guint ix)
{
Magazine *mag = &tmem->magazine1[ix];
g_assert (mag->chunks == NULL); /* ensure that we may reset mag->count */
mag->count = 0;
mag->chunks = magazine_cache_pop_magazine (ix, &mag->count);
}
static void
thread_memory_magazine2_unload (ThreadMemory *tmem,
guint ix)
{
Magazine *mag = &tmem->magazine2[ix];
magazine_cache_push_magazine (ix, mag->chunks, mag->count);
mag->chunks = NULL;
mag->count = 0;
}
static inline void
thread_memory_swap_magazines (ThreadMemory *tmem,
guint ix)
{
Magazine xmag = tmem->magazine1[ix];
tmem->magazine1[ix] = tmem->magazine2[ix];
tmem->magazine2[ix] = xmag;
}
static inline gboolean
thread_memory_magazine1_is_empty (ThreadMemory *tmem,
guint ix)
{
return tmem->magazine1[ix].chunks == NULL;
}
static inline gboolean
thread_memory_magazine2_is_full (ThreadMemory *tmem,
guint ix)
{
return tmem->magazine2[ix].count >= allocator_get_magazine_threshold (allocator, ix);
}
static inline gpointer
thread_memory_magazine1_alloc (ThreadMemory *tmem,
guint ix)
{
Magazine *mag = &tmem->magazine1[ix];
ChunkLink *chunk = magazine_chain_pop_head (&mag->chunks);
if (G_LIKELY (mag->count > 0))
mag->count--;
return chunk;
}
static inline void
thread_memory_magazine2_free (ThreadMemory *tmem,
guint ix,
gpointer mem)
{
Magazine *mag = &tmem->magazine2[ix];
ChunkLink *chunk = mem;
chunk->data = NULL;
chunk->next = mag->chunks;
mag->chunks = chunk;
mag->count++;
}
/* --- API functions --- */
gpointer
g_slice_alloc (gsize mem_size)
{
gsize chunk_size;
gpointer mem;
guint acat;
chunk_size = P2ALIGN (mem_size);
acat = allocator_categorize (chunk_size);
if (G_LIKELY (acat == 1)) /* allocate through magazine layer */
{
ThreadMemory *tmem = thread_memory_from_self();
guint ix = SLAB_INDEX (allocator, chunk_size);
if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))
{
thread_memory_swap_magazines (tmem, ix);
if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))
thread_memory_magazine1_reload (tmem, ix);
}
mem = thread_memory_magazine1_alloc (tmem, ix);
}
else if (acat == 2) /* allocate through slab allocator */
{
g_mutex_lock (allocator->slab_mutex);
mem = slab_allocator_alloc_chunk (chunk_size);
g_mutex_unlock (allocator->slab_mutex);
}
else /* delegate to system malloc */
mem = g_malloc (mem_size);
return mem;
}
gpointer
g_slice_alloc0 (guint mem_size)
{
gpointer mem = g_slice_alloc (mem_size);
if (mem)
memset (mem, 0, mem_size);
return mem;
}
void
g_slice_free1 (guint mem_size,
gpointer mem_block)
{
guint chunk_size = P2ALIGN (mem_size);
guint acat = allocator_categorize (chunk_size);
if (G_UNLIKELY (!mem_block))
/* pass */;
else if (G_LIKELY (acat == 1)) /* allocate through magazine layer */
{
ThreadMemory *tmem = thread_memory_from_self();
guint ix = SLAB_INDEX (allocator, chunk_size);
if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
{
thread_memory_swap_magazines (tmem, ix);
if (G_UNLIKELY (thread_memory_magazine2_is_full (tmem, ix)))
thread_memory_magazine2_unload (tmem, ix);
}
thread_memory_magazine2_free (tmem, ix, mem_block);
}
else if (acat == 2) /* allocate through slab allocator */
{
g_mutex_lock (allocator->slab_mutex);
slab_allocator_free_chunk (chunk_size, mem_block);
g_mutex_unlock (allocator->slab_mutex);
}
else /* delegate to system malloc */
g_free (mem_block);
}
void
g_slice_free_chain (guint mem_size,
gpointer mem_chain,
guint next_offset)
{
GSList *slice = mem_chain;
g_return_if_fail (next_offset == G_STRUCT_OFFSET (GSList, next));
g_return_if_fail (mem_size >= sizeof (GSList));
while (slice)
{
GSList *current = slice;
slice = slice->next;
g_slice_free1 (mem_size, current);
}
/* while the thread magazines and the magazine cache are implemented so that
* they can easily be extended to allow for free lists containing more free
* lists for the first level nodes, which would allow O(1) freeing in this
* function, the benefit of such an extension is questionable, because:
* - the magazine size counts will become mere lower bounds which confuses
* the code adapting to lock contention;
* - freeing a single node to the thread magazines is very fast, so this
* O(list_length) operation is multiplied by a fairly small factor;
* - memory usage histograms on larger applications seem to indicate that
* the amount of released multi node lists is negligible in comparison
* to single node releases.
*/
}
/* --- single page allocator --- */
static void
allocator_slab_stack_push (Allocator *allocator,
guint ix,
SlabInfo *sinfo)
{
/* insert slab at slab ring head */
if (!allocator->slab_stack[ix])
{
sinfo->next = sinfo;
sinfo->prev = sinfo;
}
else
{
SlabInfo *next = allocator->slab_stack[ix], *prev = next->prev;
next->prev = sinfo;
prev->next = sinfo;
sinfo->next = next;
sinfo->prev = prev;
}
allocator->slab_stack[ix] = sinfo;
}
static void
allocator_add_slab (Allocator *allocator,
guint ix,
guint chunk_size)
{
SlabInfo *sinfo;
gsize padding, n_chunks, color = 0;
gsize page_size = SLAB_PAGE_SIZE (allocator, chunk_size);
/* allocate 1 page for the chunks and the slab */
gpointer aligned_memory = allocator_memalign (page_size, page_size - NATIVE_MALLOC_PADDING);
guint8 *mem = aligned_memory;
if (!mem)
g_error ("%s: failed to allocate %lu bytes: %s", "GSlicedMemory", (gulong) (page_size - NATIVE_MALLOC_PADDING), g_strerror (errno));
/* mask page adress */
gsize addr = ((gsize) mem / page_size) * page_size;
/* assert alignment */
g_assert (aligned_memory == (gpointer) addr);
/* basic slab info setup */
sinfo = (SlabInfo*) (mem + page_size - SLAB_INFO_SIZE);
sinfo->n_allocated = 0;
sinfo->chunks = NULL;
/* figure cache colorization */
n_chunks = ((guint8*) sinfo - mem) / chunk_size;
padding = ((guint8*) sinfo - mem) - n_chunks * chunk_size;
if (padding)
{
color = (allocator->color_accu * P2ALIGNMENT) % padding;
allocator->color_accu += 1; /* alternatively: + 0x7fffffff */
}
/* add chunks to free list */
ChunkLink *chunk = (ChunkLink*) (mem + color);
guint i;
sinfo->chunks = chunk;
for (i = 0; i < n_chunks - 1; i++)
{
chunk->next = (ChunkLink*) ((guint8*) chunk + chunk_size);
chunk = chunk->next;
}
chunk->next = NULL; /* last chunk */
/* add slab to slab ring */
allocator_slab_stack_push (allocator, ix, sinfo);
}
static gpointer
slab_allocator_alloc_chunk (guint chunk_size)
{
guint ix = SLAB_INDEX (allocator, chunk_size);
/* ensure non-empty slab */
if (!allocator->slab_stack[ix] || !allocator->slab_stack[ix]->chunks)
allocator_add_slab (allocator, ix, chunk_size);
/* allocate chunk */
ChunkLink *chunk = allocator->slab_stack[ix]->chunks;
allocator->slab_stack[ix]->chunks = chunk->next;
allocator->slab_stack[ix]->n_allocated++;
/* rotate empty slabs */
if (!allocator->slab_stack[ix]->chunks)
allocator->slab_stack[ix] = allocator->slab_stack[ix]->next;
return chunk;
}
static void
slab_allocator_free_chunk (guint chunk_size,
gpointer mem)
{
guint ix = SLAB_INDEX (allocator, chunk_size);
gsize page_size = SLAB_PAGE_SIZE (allocator, chunk_size);
gsize addr = ((gsize) mem / page_size) * page_size;
/* mask page adress */
guint8 *page = (guint8*) addr;
SlabInfo *sinfo = (SlabInfo*) (page + page_size - SLAB_INFO_SIZE);
/* assert valid chunk count */
g_assert (sinfo->n_allocated > 0);
/* add chunk to free list */
gboolean was_empty = sinfo->chunks == NULL;
ChunkLink *chunk = (ChunkLink*) mem;
chunk->next = sinfo->chunks;
sinfo->chunks = chunk;
sinfo->n_allocated--;
/* keep slab ring partially sorted, empty slabs at end */
if (was_empty)
{
/* unlink slab */
SlabInfo *next = sinfo->next, *prev = sinfo->prev;
next->prev = prev;
prev->next = next;
if (allocator->slab_stack[ix] == sinfo)
allocator->slab_stack[ix] = next == sinfo ? NULL : next;
/* insert slab at head */
allocator_slab_stack_push (allocator, ix, sinfo);
}
/* eagerly free complete unused slabs */
if (!sinfo->n_allocated)
{
/* unlink slab */
SlabInfo *next = sinfo->next, *prev = sinfo->prev;
next->prev = prev;
prev->next = next;
if (allocator->slab_stack[ix] == sinfo)
allocator->slab_stack[ix] = next == sinfo ? NULL : next;
/* free slab */
allocator_memfree (page_size, page);
}
}
/* --- memalign implementation --- */
#include <malloc.h> /* memalign() */
/* from config.h:
* define HAVE_POSIX_MEMALIGN 1 // if free(posix_memalign(3)) works, <stdlib.h>
* define HAVE_MEMALIGN 1 // if free(memalign(3)) works, <malloc.h>
* define HAVE_VALLOC 1 // if free(valloc(3)) works, <stdlib.h> or <malloc.h>
* if none is provided, we implement malloc(3)-based alloc-only page alignment
*/
#if !(HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC)
static GTrashStack *compat_valloc_trash = NULL;
#endif
static gpointer
allocator_memalign (gsize alignment,
gsize memsize)
{
gpointer aligned_memory = NULL;
gint err = ENOMEM;
#if HAVE_POSIX_MEMALIGN
err = posix_memalign (&aligned_memory, alignment, memsize);
#elif HAVE_MEMALIGN
errno = 0;
aligned_memory = memalign (alignment, memsize);
err = errno;
#elif HAVE_VALLOC
errno = 0;
aligned_memory = valloc (memsize);
err = errno;
#else
/* simplistic non-freeing page allocator */
g_assert (alignment == sys_page_size);
g_assert (memsize <= sys_page_size);
if (!compat_valloc_trash)
{
const guint n_pages = 16;
guint8 *mem = malloc (n_pages * sys_page_size);
err = errno;
if (mem)
{
gint i = n_pages;
guint8 *amem = (guint8*) ALIGN ((gsize) mem, sys_page_size);
if (amem != mem)
i--; /* mem wasn't page aligned */
while (--i >= 0)
g_trash_stack_push (&compat_valloc_trash, amem + i * sys_page_size);
}
}
aligned_memory = g_trash_stack_pop (&compat_valloc_trash);
#endif
if (!aligned_memory)
errno = err;
return aligned_memory;
}
static void
allocator_memfree (gsize memsize,
gpointer mem)
{
#if HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_VALLOC
free (mem);
#else
g_assert (memsize <= sys_page_size);
g_trash_stack_push (&compat_valloc_trash, mem);
#endif
}
#define __G_SLICE_C__
#include "galiasdef.c"

70
glib/gslice.h Normal file
View File

@ -0,0 +1,70 @@
/* GLIB sliced memory - fast threaded memory chunk allocator
* Copyright (C) 2005 Tim Janik
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __G_SLICE_H__
#define __G_SLICE_H__
#ifndef __G_MEM_H__
#error Include <glib.h> instead of <gslice.h>
#endif
#include <glib/gtypes.h>
G_BEGIN_DECLS
/* slices - fast allocation/release of small memory blocks
*/
gpointer g_slice_alloc (gsize block_size) G_GNUC_MALLOC;
gpointer g_slice_alloc0 (gsize block_size) G_GNUC_MALLOC;
void g_slice_free1 (gsize block_size,
gpointer mem_block);
void g_slice_free_chain (gsize block_size,
gpointer mem_chain,
gsize next_offset);
#define g_slice_new(type) ((type*) g_slice_alloc (sizeof (type)))
#define g_slice_new0(type) ((type*) g_slice_alloc0 (sizeof (type)))
/* g_slice_free(type,mem) g_slice_free1 (sizeof (type), mem) */
#if __GNUC__ >= 2
/* for GCC, define a type-safe variant of g_slice_free() */
#define g_slice_free(type, mem) ({ \
static inline void g_slice_free (gsize, type*); \
while (0) g_slice_free (sizeof (type), mem); \
g_slice_free1 (sizeof (type), mem); \
})
#else
#define g_slice_free(type, mem) g_slice_free1 (sizeof (type) + (gsize) (type*) 0, mem)
/* we go through the extra (gsize)(type*)0 hoop to ensure a known type argument */
#endif
/* --- internal debugging API --- */
typedef enum {
G_SLICE_CONFIG_ALWAYS_MALLOC = 1,
G_SLICE_CONFIG_BYPASS_MAGAZINES,
G_SLICE_CONFIG_ALWAYS_FREE,
G_SLICE_CONFIG_WORKING_SET_MSECS,
G_SLICE_CONFIG_CHUNK_SIZES,
G_SLICE_CONFIG_CONTENTION_COUNTER,
} GSliceConfig;
void g_slice_set_config (GSliceConfig ckey, gint64 value);
gint64 g_slice_get_config (GSliceConfig ckey);
gint64* g_slice_get_config_state (GSliceConfig ckey, gint64 address, guint *n_values);
G_END_DECLS
#endif /* __G_SLICE_H__ */

View File

@ -76,7 +76,6 @@ struct _GRealThread
{ {
GThread thread; GThread thread;
gpointer private_data; gpointer private_data;
gpointer mem_private;
GRealThread *next; GRealThread *next;
gpointer retval; gpointer retval;
GSystemThread system_thread; GSystemThread system_thread;
@ -145,29 +144,34 @@ g_thread_init_glib (void)
*/ */
GRealThread* main_thread = (GRealThread*) g_thread_self (); GRealThread* main_thread = (GRealThread*) g_thread_self ();
/* mutex and cond creation works without g_threads_got_initialized */
g_once_mutex = g_mutex_new (); g_once_mutex = g_mutex_new ();
g_once_cond = g_cond_new (); g_once_cond = g_cond_new ();
/* we may only create mutex and cond in here */
_g_mem_thread_init_noprivate_nomessage ();
/* setup the basic threading system */
g_threads_got_initialized = TRUE;
g_thread_specific_private = g_private_new (g_thread_cleanup);
g_private_set (g_thread_specific_private, main_thread);
G_THREAD_UF (thread_self, (&main_thread->system_thread));
/* complete memory system initialization, g_private_*() works now */
_g_slice_thread_init_nomessage ();
/* accomplish log system initialization to enable messaging */
_g_messages_thread_init_nomessage ();
/* we may run full-fledged initializers from here */
_g_convert_thread_init (); _g_convert_thread_init ();
_g_rand_thread_init (); _g_rand_thread_init ();
_g_main_thread_init (); _g_main_thread_init ();
_g_mem_thread_init ();
_g_messages_thread_init ();
_g_atomic_thread_init (); _g_atomic_thread_init ();
_g_utils_thread_init (); _g_utils_thread_init ();
#ifdef G_OS_WIN32 #ifdef G_OS_WIN32
_g_win32_thread_init (); _g_win32_thread_init ();
#endif #endif
g_threads_got_initialized = TRUE;
g_thread_specific_private = g_private_new (g_thread_cleanup);
g_private_set (g_thread_specific_private, main_thread);
G_THREAD_UF (thread_self, (&main_thread->system_thread));
_g_mem_thread_private_init ();
_g_messages_thread_private_init ();
} }
#endif /* G_THREADS_ENABLED */ #endif /* G_THREADS_ENABLED */
@ -868,28 +872,6 @@ g_static_rw_lock_free (GStaticRWLock* lock)
g_static_mutex_free (&lock->mutex); g_static_mutex_free (&lock->mutex);
} }
/*
* Memory allocation can't use the regular GPrivate
* API, since that relies on GArray, which uses
* chunked memory.
*/
gpointer
_g_thread_mem_private_get (GThread *thread)
{
GRealThread *real_thread = (GRealThread*) thread;
return real_thread->mem_private;
}
void
_g_thread_mem_private_set (GThread *thread,
gpointer data)
{
GRealThread *real_thread = (GRealThread*) thread;
real_thread->mem_private = data;
}
/** /**
* g_thread_foreach * g_thread_foreach
* @thread_func: function to call for all GThread structures * @thread_func: function to call for all GThread structures

View File

@ -370,10 +370,6 @@ extern void glib_dummy_decl (void);
# define G_TRYLOCK(name) (TRUE) # define G_TRYLOCK(name) (TRUE)
#endif /* !G_THREADS_ENABLED */ #endif /* !G_THREADS_ENABLED */
/* --- internal API --- */
gpointer _g_thread_mem_private_get (GThread *thread);
void _g_thread_mem_private_set (GThread *thread,
gpointer data);
G_END_DECLS G_END_DECLS

View File

@ -26,9 +26,13 @@ G_BEGIN_DECLS
/* Is called from gthread/gthread-impl.c */ /* Is called from gthread/gthread-impl.c */
void g_thread_init_glib (void); void g_thread_init_glib (void);
/* Are called from glib/gthread.c. May not contain g_private_new calls */ /* base initializers, may only use g_mutex_new(), g_cond_new() */
void _g_mem_thread_init (void) G_GNUC_INTERNAL; void _g_mem_thread_init_noprivate_nomessage (void) G_GNUC_INTERNAL;
void _g_messages_thread_init (void) G_GNUC_INTERNAL; /* initializers that may also use g_private_new() */
void _g_slice_thread_init_nomessage (void) G_GNUC_INTERNAL;
void _g_messages_thread_init_nomessage (void) G_GNUC_INTERNAL;
/* full fledged initializersa */
void _g_convert_thread_init (void) G_GNUC_INTERNAL; void _g_convert_thread_init (void) G_GNUC_INTERNAL;
void _g_rand_thread_init (void) G_GNUC_INTERNAL; void _g_rand_thread_init (void) G_GNUC_INTERNAL;
void _g_main_thread_init (void) G_GNUC_INTERNAL; void _g_main_thread_init (void) G_GNUC_INTERNAL;
@ -38,8 +42,10 @@ void _g_utils_thread_init (void) G_GNUC_INTERNAL;
void _g_win32_thread_init (void) G_GNUC_INTERNAL; void _g_win32_thread_init (void) G_GNUC_INTERNAL;
#endif #endif
/* Are called from glib/gthread.c. Must only contain g_private_new calls */ /* initialization functions called from glib/gthread.c.
void _g_mem_thread_private_init (void) G_GNUC_INTERNAL; * may contain g_mutex_new().
* may contain g_private_new() calls.
*/
void _g_messages_thread_private_init (void) G_GNUC_INTERNAL; void _g_messages_thread_private_init (void) G_GNUC_INTERNAL;
G_END_DECLS G_END_DECLS

View File

@ -91,6 +91,7 @@ test_programs = \
relation-test \ relation-test \
shell-test \ shell-test \
slist-test \ slist-test \
slice-test \
spawn-test \ spawn-test \
$(spawn_test_win32_gui) \ $(spawn_test_win32_gui) \
strfunc-test \ strfunc-test \
@ -150,6 +151,7 @@ rand_test_LDADD = $(progs_ldadd)
relation_test_LDADD = $(progs_ldadd) relation_test_LDADD = $(progs_ldadd)
shell_test_LDADD = $(progs_ldadd) shell_test_LDADD = $(progs_ldadd)
slist_test_LDADD = $(progs_ldadd) slist_test_LDADD = $(progs_ldadd)
slice_test_LDADD = $(thread_ldadd)
spawn_test_LDADD = $(progs_ldadd) spawn_test_LDADD = $(progs_ldadd)
strfunc_test_LDADD = $(progs_ldadd) strfunc_test_LDADD = $(progs_ldadd)
string_test_LDADD = $(progs_ldadd) string_test_LDADD = $(progs_ldadd)

168
tests/slice-test.c Normal file
View File

@ -0,0 +1,168 @@
/* GLIB sliced memory - fast threaded memory chunk allocator
* Copyright (C) 2005 Tim Janik
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <glib.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h> // gettimeofday
#define quick_rand32() (rand_accu = 1664525 * rand_accu + 1013904223, rand_accu)
static guint prime_size = 1021; // 769; // 509
static gpointer
test_sliced_mem_thread (gpointer data)
{
guint32 rand_accu = 2147483563;
/* initialize random numbers */
if (data)
rand_accu = *(guint32*) data;
else
{
struct timeval rand_tv;
gettimeofday (&rand_tv, NULL);
rand_accu = rand_tv.tv_usec + (rand_tv.tv_sec << 16);
}
guint i, m = 10000; /* number of blocks */
guint j, n = 10000; /* number of alloc+free repetitions */
guint8 **ps = g_new (guint8*, m);
guint *ss = g_new (guint, m);
/* create m random sizes */
for (i = 0; i < m; i++)
ss[i] = quick_rand32() % prime_size;
/* allocate m blocks */
for (i = 0; i < m; i++)
ps[i] = g_slice_alloc (ss[i]);
for (j = 0; j < n; j++)
{
/* free m/2 blocks */
for (i = 0; i < m; i += 2)
g_slice_free1 (ss[i], ps[i]);
/* allocate m/2 blocks with new sizes */
for (i = 0; i < m; i += 2)
{
ss[i] = quick_rand32() % prime_size;
ps[i] = g_slice_alloc (ss[i]);
}
}
/* free m blocks */
for (i = 0; i < m; i++)
g_slice_free1 (ss[i], ps[i]);
/* alloc and free many equally sized chunks in a row */
for (i = 0; i < n; i++)
{
guint sz = quick_rand32() % prime_size;
guint k = m / 100;
for (j = 0; j < k; j++)
ps[j] = g_slice_alloc (sz);
for (j = 0; j < k; j++)
g_slice_free1 (sz, ps[j]);
}
return NULL;
}
static void
usage (void)
{
g_print ("Usage: gslicedmemory [n_threads] [G|S|M][f][c] [maxblocksize] [seed]\n");
}
int
main (int argc,
char *argv[])
{
guint seed32, *seedp = NULL;
gboolean ccounters = FALSE;
guint n_threads = 1;
const gchar *mode = "slab allocator + magazine cache", *emode = " ";
if (argc > 1)
n_threads = g_ascii_strtoull (argv[1], NULL, 10);
if (argc > 2)
{
guint i, l = strlen (argv[2]);
for (i = 0; i < l; i++)
switch (argv[2][i])
{
case 'G': /* GLib mode */
g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, FALSE);
mode = "slab allocator + magazine cache";
break;
case 'S': /* slab mode */
g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, FALSE);
g_slice_set_config (G_SLICE_CONFIG_BYPASS_MAGAZINES, TRUE);
mode = "slab allocator";
break;
case 'M': /* malloc mode */
g_slice_set_config (G_SLICE_CONFIG_ALWAYS_MALLOC, TRUE);
mode = "system malloc";
break;
case 'f': /* eager freeing */
g_slice_set_config (G_SLICE_CONFIG_ALWAYS_FREE, TRUE);
emode = " with eager freeing";
break;
case 'c': /* print contention counters */
ccounters = TRUE;
break;
default:
usage();
return 1;
}
}
if (argc > 3)
prime_size = g_ascii_strtoull (argv[3], NULL, 10);
if (argc > 4)
{
seed32 = g_ascii_strtoull (argv[4], NULL, 10);
seedp = &seed32;
}
g_thread_init (NULL);
if (argc <= 1)
usage();
gchar strseed[64] = "<random>";
if (seedp)
g_snprintf (strseed, 64, "%u", *seedp);
g_print ("Starting %d threads allocating random blocks <= %u bytes with seed=%s using %s%s\n", n_threads, prime_size, strseed, mode, emode);
GThread *threads[n_threads];
guint i;
for (i = 0; i < n_threads; i++)
threads[i] = g_thread_create_full (test_sliced_mem_thread, seedp, 0, TRUE, FALSE, 0, NULL);
for (i = 0; i < n_threads; i++)
g_thread_join (threads[i]);
if (ccounters)
{
guint n, n_chunks = g_slice_get_config (G_SLICE_CONFIG_CHUNK_SIZES);
g_print (" ChunkSize | MagazineSize | Contention\n");
for (i = 0; i < n_chunks; i++)
{
gint64 *vals = g_slice_get_config_state (G_SLICE_CONFIG_CONTENTION_COUNTER, i, &n);
g_print (" %9llu | %9llu | %9llu\n", vals[0], vals[2], vals[1]);
g_free (vals);
}
}
else
g_print ("Done.\n");
return 0;
}