From 5617c0eacfa4537a7205928d9ae3e642a81ea6e8 Mon Sep 17 00:00:00 2001 From: obarthel Date: Sun, 27 Nov 2016 15:53:40 +0100 Subject: [PATCH] Slab allocator update Unused slabs which get recycled are no longer reinitialized from scratch if their chunk size matches what the allocator needed. If the chunk size matches, the list of available chunks is left unchanged, and just the various counters are reset. Added __get_slab_stats() function. Added support for global __slab_purge_threshold tuning variable. Added a short test program for the slab allocator. The malloc-test program was linked against the wrong object file in GNUmakefile.68k. Fixed. --- library/GNUmakefile.68k | 26 ++-- library/amiga.lib_rev.h | 10 +- library/amiga.lib_rev.rev | 2 +- library/c.lib_rev.h | 10 +- library/c.lib_rev.rev | 2 +- library/changes | 12 ++ library/debug.lib_rev.h | 10 +- library/debug.lib_rev.rev | 2 +- library/include/stdlib.h | 43 ++++++ library/libc.gmk | 2 + library/m.lib_rev.h | 10 +- library/m.lib_rev.rev | 2 +- library/m881.lib_rev.h | 10 +- library/m881.lib_rev.rev | 2 +- library/net.lib_rev.h | 10 +- library/net.lib_rev.rev | 2 +- library/smakefile | 6 + library/stdlib_get_slab_stats.c | 200 ++++++++++++++++++++++++++ library/stdlib_get_slab_usage.c | 1 + library/stdlib_malloc.c | 2 +- library/stdlib_memory.h | 8 +- library/stdlib_realloc.c | 2 + library/stdlib_slab.c | 177 +++++++++++++++++------ library/stdlib_slab_purge_threshold.c | 38 +++++ library/unix.lib_rev.h | 10 +- library/unix.lib_rev.rev | 2 +- test_programs/GNUmakefile.68k | 16 ++- test_programs/slab-test.c | 26 ++++ 28 files changed, 538 insertions(+), 105 deletions(-) create mode 100644 library/stdlib_get_slab_stats.c create mode 100644 library/stdlib_slab_purge_threshold.c create mode 100644 test_programs/slab-test.c diff --git a/library/GNUmakefile.68k b/library/GNUmakefile.68k index e908b5b..379881c 100644 --- a/library/GNUmakefile.68k +++ b/library/GNUmakefile.68k @@ -334,6 +334,7 @@ C_LIB = \ stdlib_get_errno.o \ stdlib_get_slab_usage.o \ stdlib_get_slab_allocations.o \ + stdlib_get_slab_stats.o \ stdlib_isresident.o \ stdlib_labs.o \ stdlib_llabs.o \ @@ -373,6 +374,7 @@ C_LIB = \ stdlib_showerror.o \ stdlib_slab.o \ stdlib_slab_max_size.o \ + stdlib_slab_purge_threshold.o \ stdlib_srand.o \ stdlib_stacksize.o \ stdlib_stack_usage.o \ @@ -1124,25 +1126,29 @@ $(LIBC_OBJS)/stdlib_getdefstacksize.o : stdlib_getdefstacksize.c stdlib_gcc_help $(LIBC_OBJS)/stdlib_shell_escape.o : stdlib_shell_escape.c stdlib_gcc_help.h -$(LIBC_OBJS)/stdlib_alloca.o : stdlib_alloca.c stdlib_memory.h +$(LIBC_OBJS)/stdlib_alloca.o : stdlib_alloca.c stdlib_memory.h include/stdlib.h -$(LIBC_OBJS)/stdlib_calloc.o : stdlib_calloc.c stdlib_memory.h +$(LIBC_OBJS)/stdlib_calloc.o : stdlib_calloc.c stdlib_memory.h include/stdlib.h -$(LIBC_OBJS)/stdlib_free.o : stdlib_free.c stdlib_memory.h +$(LIBC_OBJS)/stdlib_free.o : stdlib_free.c stdlib_memory.h include/stdlib.h -$(LIBC_OBJS)/stdlib_malloc.o : stdlib_malloc.c stdlib_memory.h +$(LIBC_OBJS)/stdlib_malloc.o : stdlib_malloc.c stdlib_memory.h include/stdlib.h -$(LIBC_OBJS)/stdlib_slab.o : stdlib_slab.c stdlib_memory.h +$(LIBC_OBJS)/stdlib_slab.o : stdlib_slab.c stdlib_memory.h include/stdlib.h -$(LIBC_OBJS)/stdlib_free_unused_slabs.o : stdlib_free_unused_slabs.c stdlib_memory.h +$(LIBC_OBJS)/stdlib_slab_purge_threshold.o : stdlib_slab_purge_threshold.c stdlib_memory.h include/stdlib.h -$(LIBC_OBJS)/stdlib_get_slab_usage.o : stdlib_get_slab_usage.c stdlib_memory.h +$(LIBC_OBJS)/stdlib_get_slab_stats.o : stdlib_get_slab_stats.c stdlib_memory.h include/stdlib.h -$(LIBC_OBJS)/stdlib_get_slab_allocations.o : stdlib_get_slab_allocations.c stdlib_memory.h +$(LIBC_OBJS)/stdlib_free_unused_slabs.o : stdlib_free_unused_slabs.c stdlib_memory.h include/stdlib.h -$(LIBC_OBJS)/stdlib_realloc.o : stdlib_realloc.c stdlib_memory.h +$(LIBC_OBJS)/stdlib_get_slab_usage.o : stdlib_get_slab_usage.c stdlib_memory.h include/stdlib.h -$(LIBC_OBJS)/stdlib_red_black.o : stdlib_red_black.c stdlib_memory.h +$(LIBC_OBJS)/stdlib_get_slab_allocations.o : stdlib_get_slab_allocations.c stdlib_memory.h include/stdlib.h + +$(LIBC_OBJS)/stdlib_realloc.o : stdlib_realloc.c stdlib_memory.h include/stdlib.h + +$(LIBC_OBJS)/stdlib_red_black.o : stdlib_red_black.c stdlib_memory.h include/stdlib.h ############################################################################## diff --git a/library/amiga.lib_rev.h b/library/amiga.lib_rev.h index 52f4fff..446709b 100644 --- a/library/amiga.lib_rev.h +++ b/library/amiga.lib_rev.h @@ -1,6 +1,6 @@ #define VERSION 1 -#define REVISION 211 -#define DATE "23.11.2016" -#define VERS "amiga.lib 1.211" -#define VSTRING "amiga.lib 1.211 (23.11.2016)\r\n" -#define VERSTAG "\0$VER: amiga.lib 1.211 (23.11.2016)" +#define REVISION 212 +#define DATE "27.11.2016" +#define VERS "amiga.lib 1.212" +#define VSTRING "amiga.lib 1.212 (27.11.2016)\r\n" +#define VERSTAG "\0$VER: amiga.lib 1.212 (27.11.2016)" diff --git a/library/amiga.lib_rev.rev b/library/amiga.lib_rev.rev index dba40af..0d38910 100644 --- a/library/amiga.lib_rev.rev +++ b/library/amiga.lib_rev.rev @@ -1 +1 @@ -211 +212 diff --git a/library/c.lib_rev.h b/library/c.lib_rev.h index 27254de..72cda99 100644 --- a/library/c.lib_rev.h +++ b/library/c.lib_rev.h @@ -1,6 +1,6 @@ #define VERSION 1 -#define REVISION 211 -#define DATE "23.11.2016" -#define VERS "c.lib 1.211" -#define VSTRING "c.lib 1.211 (23.11.2016)\r\n" -#define VERSTAG "\0$VER: c.lib 1.211 (23.11.2016)" +#define REVISION 212 +#define DATE "27.11.2016" +#define VERS "c.lib 1.212" +#define VSTRING "c.lib 1.212 (27.11.2016)\r\n" +#define VERSTAG "\0$VER: c.lib 1.212 (27.11.2016)" diff --git a/library/c.lib_rev.rev b/library/c.lib_rev.rev index dba40af..0d38910 100644 --- a/library/c.lib_rev.rev +++ b/library/c.lib_rev.rev @@ -1 +1 @@ -211 +212 diff --git a/library/changes b/library/changes index 96246af..aebc0bf 100644 --- a/library/changes +++ b/library/changes @@ -1,3 +1,15 @@ +c.lib 1.212 (27.11.2016) + +- Unused slabs which get recycled are no longer reinitialized from + scratch if their chunk size matches what the allocator needed. + If the chunk size matches, the list of available chunks is + left unchanged, and just the various counters are reset. + +- Added __get_slab_stats() function. + +- Added support for global __slab_purge_threshold tuning variable. + + c.lib 1.211 (23.11.2016) - Added more consistency checking to the slab allocator, which is diff --git a/library/debug.lib_rev.h b/library/debug.lib_rev.h index 7d2582c..ce1a63c 100644 --- a/library/debug.lib_rev.h +++ b/library/debug.lib_rev.h @@ -1,6 +1,6 @@ #define VERSION 1 -#define REVISION 211 -#define DATE "23.11.2016" -#define VERS "debug.lib 1.211" -#define VSTRING "debug.lib 1.211 (23.11.2016)\r\n" -#define VERSTAG "\0$VER: debug.lib 1.211 (23.11.2016)" +#define REVISION 212 +#define DATE "27.11.2016" +#define VERS "debug.lib 1.212" +#define VSTRING "debug.lib 1.212 (27.11.2016)\r\n" +#define VERSTAG "\0$VER: debug.lib 1.212 (27.11.2016)" diff --git a/library/debug.lib_rev.rev b/library/debug.lib_rev.rev index dba40af..0d38910 100644 --- a/library/debug.lib_rev.rev +++ b/library/debug.lib_rev.rev @@ -1 +1 @@ -211 +212 diff --git a/library/include/stdlib.h b/library/include/stdlib.h index 415c584..44a4559 100644 --- a/library/include/stdlib.h +++ b/library/include/stdlib.h @@ -173,6 +173,19 @@ extern int rand_r(unsigned int * seed); extern unsigned long __slab_max_size; +/* + * The slab allocator will periodically free all currently unused memory. + * You can control how much memory should be released, instead of + * releasing everything. + * + * This would make the slab allocator release only up to 512 KBytes of + * unused memory at a time: + * + * unsigned long __slab_purge_threshold = 512 * 1024; + */ + +extern unsigned long __slab_purge_threshold; + /****************************************************************************/ /* @@ -250,6 +263,11 @@ struct __slab_usage_information /* How many memory chunks in this slab are being used? */ size_t sui_num_chunks_used; + + /* How many time was this slab reused without reinitializing + * it all over again from scratch? + */ + size_t sui_num_reused; }; /****************************************************************************/ @@ -324,6 +342,31 @@ void __get_slab_allocations(__slab_allocation_callback callback); /****************************************************************************/ +/* + * You can obtain information about the memory managed by the slab allocator, + * as well as additional information about the slab allocator's performance + * in JSON format. This format can be used for more detailed analysis. + * + * You supply a function which will be called for each line of the JSON + * data produced. You can store this data in a file, or in the clipboard, + * for later use. Your function must return 0 if it wants to be called + * again, or return -1 if it wants to stop (e.g. if an error occured + * when writing the JSON data to disk). The same "user_data" pointer which + * you pass to __get_slab_stats() will be passed to your callback function. + * + * Please note that this function works within the context of the memory + * allocation system and is not safe to call from interrupt code. It may + * break a Forbid() or Disable() condition. + */ + +typedef int (* __slab_status_callback)(void * user_data, const char * line, size_t line_length); + +/****************************************************************************/ + +extern void __get_slab_stats(void * user_data, __slab_status_callback callback); + +/****************************************************************************/ + /* * You can request to use the alloca() variant that actually does allocate * memory from the system rather than the current stack frame, which will diff --git a/library/libc.gmk b/library/libc.gmk index ff693e5..eb28adf 100644 --- a/library/libc.gmk +++ b/library/libc.gmk @@ -220,6 +220,7 @@ C_LIB := \ stdlib_get_errno.o \ stdlib_get_slab_usage.o \ stdlib_get_slab_allocations.o \ + stdlib_get_slab_stats.o \ stdlib_isresident.o \ stdlib_labs.o \ stdlib_llabs.o \ @@ -260,6 +261,7 @@ C_LIB := \ stdlib_showerror.o \ stdlib_slab.o \ stdlib_slab_max_size.o \ + stdlib_slab_purge_threshold.o \ stdlib_srand.o \ stdlib_stacksize.o \ stdlib_stack_usage.o \ diff --git a/library/m.lib_rev.h b/library/m.lib_rev.h index 2c650af..7ebc7a0 100644 --- a/library/m.lib_rev.h +++ b/library/m.lib_rev.h @@ -1,6 +1,6 @@ #define VERSION 1 -#define REVISION 211 -#define DATE "23.11.2016" -#define VERS "m.lib 1.211" -#define VSTRING "m.lib 1.211 (23.11.2016)\r\n" -#define VERSTAG "\0$VER: m.lib 1.211 (23.11.2016)" +#define REVISION 212 +#define DATE "27.11.2016" +#define VERS "m.lib 1.212" +#define VSTRING "m.lib 1.212 (27.11.2016)\r\n" +#define VERSTAG "\0$VER: m.lib 1.212 (27.11.2016)" diff --git a/library/m.lib_rev.rev b/library/m.lib_rev.rev index dba40af..0d38910 100644 --- a/library/m.lib_rev.rev +++ b/library/m.lib_rev.rev @@ -1 +1 @@ -211 +212 diff --git a/library/m881.lib_rev.h b/library/m881.lib_rev.h index 7575097..2e11c0b 100644 --- a/library/m881.lib_rev.h +++ b/library/m881.lib_rev.h @@ -1,6 +1,6 @@ #define VERSION 1 -#define REVISION 211 -#define DATE "23.11.2016" -#define VERS "m881.lib 1.211" -#define VSTRING "m881.lib 1.211 (23.11.2016)\r\n" -#define VERSTAG "\0$VER: m881.lib 1.211 (23.11.2016)" +#define REVISION 212 +#define DATE "27.11.2016" +#define VERS "m881.lib 1.212" +#define VSTRING "m881.lib 1.212 (27.11.2016)\r\n" +#define VERSTAG "\0$VER: m881.lib 1.212 (27.11.2016)" diff --git a/library/m881.lib_rev.rev b/library/m881.lib_rev.rev index dba40af..0d38910 100644 --- a/library/m881.lib_rev.rev +++ b/library/m881.lib_rev.rev @@ -1 +1 @@ -211 +212 diff --git a/library/net.lib_rev.h b/library/net.lib_rev.h index eb63df2..d4f345a 100644 --- a/library/net.lib_rev.h +++ b/library/net.lib_rev.h @@ -1,6 +1,6 @@ #define VERSION 1 -#define REVISION 211 -#define DATE "23.11.2016" -#define VERS "net.lib 1.211" -#define VSTRING "net.lib 1.211 (23.11.2016)\r\n" -#define VERSTAG "\0$VER: net.lib 1.211 (23.11.2016)" +#define REVISION 212 +#define DATE "27.11.2016" +#define VERS "net.lib 1.212" +#define VSTRING "net.lib 1.212 (27.11.2016)\r\n" +#define VERSTAG "\0$VER: net.lib 1.212 (27.11.2016)" diff --git a/library/net.lib_rev.rev b/library/net.lib_rev.rev index dba40af..0d38910 100644 --- a/library/net.lib_rev.rev +++ b/library/net.lib_rev.rev @@ -1 +1 @@ -211 +212 diff --git a/library/smakefile b/library/smakefile index 8c7953c..38fa183 100644 --- a/library/smakefile +++ b/library/smakefile @@ -521,6 +521,7 @@ STDLIB_OBJ = \ stdlib_get_errno.o \ stdlib_get_slab_usage.o \ stdlib_get_slab_allocations.o \ + stdlib_get_slab_stats.o \ stdlib_isresident.o \ stdlib_labs.o \ stdlib_ldiv.o \ @@ -552,6 +553,7 @@ STDLIB_OBJ = \ stdlib_showerror.o \ stdlib_slab.o \ stdlib_slab_max_size.o \ + stdlib_slab_purge_threshold.o \ stdlib_srand.o \ stdlib_arg.o \ stdlib_stack_usage.o \ @@ -804,6 +806,10 @@ stdlib_slab.o : stdlib_slab.c stdlib_memory.h stdlib_free_unused_slabs.o : stdlib_free_unused_slabs.c stdlib_memory.h +stdlib_slab_max_size.o : stdlib_slab_max_size.c stdlib_memory.h + +stdlib_slab_purge_threshold.o : stdlib_slab_purge_threshold.o stdlib_memory.h + stdlib_get_slab_usage.o : stdlib_get_slab_usage.c stdlib_memory.h stdlib_realloc.o : stdlib_realloc.c stdlib_memory.h diff --git a/library/stdlib_get_slab_stats.c b/library/stdlib_get_slab_stats.c new file mode 100644 index 0000000..16b3340 --- /dev/null +++ b/library/stdlib_get_slab_stats.c @@ -0,0 +1,200 @@ +/* + * :ts=4 + * + * Portable ISO 'C' (1994) runtime library for the Amiga computer + * Copyright (c) 2002-2015 by Olaf Barthel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Neither the name of Olaf Barthel nor the names of contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _STDLIB_HEADERS_H +#include "stdlib_headers.h" +#endif /* _STDLIB_HEADERS_H */ + +/****************************************************************************/ + +#ifndef _STDLIB_MEMORY_H +#include "stdlib_memory.h" +#endif /* _STDLIB_MEMORY_H */ + +/****************************************************************************/ + +struct context +{ + int status; + void * user_data; + __slab_status_callback callback; + char * buffer; + size_t buffer_size; +}; + +/****************************************************************************/ + +static void print(struct context * ct, const char * format, ...) +{ + if(ct->status == 0) + { + va_list args; + int len; + + va_start(args,format); + len = vsnprintf(ct->buffer, ct->buffer_size, format, args); + va_end(args); + + /* This shouldn't happen: the buffer ought to be large enough + * to hold every single line. + */ + if(len >= (int)ct->buffer_size) + len = strlen(ct->buffer); + + ct->status = (*ct->callback)(ct->user_data, ct->buffer, len); + } +} + +/****************************************************************************/ + +void +__get_slab_stats(void * user_data, __slab_status_callback callback) +{ + if(__slab_data.sd_InUse) + { + static int times_checked = 1; + + const struct SlabNode * sn; + size_t num_empty_slabs = 0; + size_t num_full_slabs = 0; + size_t num_slabs = 0; + size_t slab_allocation_size = 0; + size_t total_slab_allocation_size = 0; + struct context ct; + char line[1024]; + char time_buffer[40]; + time_t now; + struct tm when; + int i; + + memset(&ct, 0, sizeof(ct)); + + ct.user_data = user_data; + ct.callback = callback; + ct.buffer = line; + ct.buffer_size = sizeof(line); + + __memory_lock(); + + now = time(NULL); + localtime_r(&now, &when); + + strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%H:%M:%S", &when); + + print(&ct,"{\n"); + + print(&ct,"\t\"when\": \"%s\",\n", time_buffer); + print(&ct,"\t\"times_checked\": %d,\n", times_checked++); + print(&ct,"\t\"slab_size\": %zu,\n", __slab_data.sd_StandardSlabSize); + print(&ct,"\t\"num_single_allocations\": %zu,\n", __slab_data.sd_NumSingleAllocations); + print(&ct,"\t\"total_single_allocation_size\": %zu,\n", __slab_data.sd_TotalSingleAllocationSize); + + if(__slab_data.sd_SingleAllocations.mlh_Head->mln_Succ != NULL) + { + const struct SlabSingleAllocation * ssa; + + print(&ct,"\t\"single_allocations\": [\n"); + + for(ssa = (struct SlabSingleAllocation *)__slab_data.sd_SingleAllocations.mlh_Head ; + ssa->ssa_MinNode.mln_Succ != NULL && ct.status == 0 ; + ssa = (struct SlabSingleAllocation *)ssa->ssa_MinNode.mln_Succ) + { + print(&ct,"\t\t{ \"size\": %lu, \"total_size\": %lu }%s\n", + ssa->ssa_Size - sizeof(*ssa), ssa->ssa_Size, + ssa->ssa_MinNode.mln_Succ->mln_Succ != NULL ? "," : ""); + } + + print(&ct,"\t],\n"); + } + else + { + print(&ct,"\t\"single_allocations\": [],\n"); + } + + for(i = 0 ; i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; i++) + { + for(sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ; + sn->sn_MinNode.mln_Succ != NULL ; + sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ) + { + if (sn->sn_UseCount == 0) + num_empty_slabs++; + else if (sn->sn_UseCount == sn->sn_Count) + num_full_slabs++; + + num_slabs++; + + slab_allocation_size += sn->sn_ChunkSize * sn->sn_UseCount; + total_slab_allocation_size += sizeof(*sn) + __slab_data.sd_StandardSlabSize; + } + } + + print(&ct,"\t\"num_slabs\": %zu,\n", num_slabs); + print(&ct,"\t\"num_empty_slabs\": %zu,\n", num_empty_slabs); + print(&ct,"\t\"num_full_slabs\": %zu,\n", num_full_slabs); + print(&ct,"\t\"slab_allocation_size\": %zu,\n", slab_allocation_size); + print(&ct,"\t\"total_slab_allocation_size\": %zu,\n", total_slab_allocation_size); + + if(num_slabs > 0) + { + const char * eol = ""; + + print(&ct,"\t\"slabs\": [\n"); + + for(i = 0 ; i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) && ct.status == 0 ; i++) + { + for(sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ; + sn->sn_MinNode.mln_Succ != NULL && ct.status == 0 ; + sn = (struct SlabNode *)sn->sn_MinNode.mln_Succ) + { + print(&ct,"%s\t\t{ \"size\": %lu, \"chunks\": %lu, \"chunks_in_use\": %lu, \"times_reused\": %lu }", + eol, + sn->sn_ChunkSize, + sn->sn_Count, + sn->sn_UseCount, + sn->sn_NumReused); + + eol = ",\n"; + } + } + + print(&ct,"\n\t]\n"); + } + else + { + print(&ct,"\t\"slabs\": []\n"); + } + + print(&ct,"}\n"); + + __memory_unlock(); + } +} diff --git a/library/stdlib_get_slab_usage.c b/library/stdlib_get_slab_usage.c index 41c4d44..9adbc48 100644 --- a/library/stdlib_get_slab_usage.c +++ b/library/stdlib_get_slab_usage.c @@ -87,6 +87,7 @@ __get_slab_usage(__slab_usage_callback callback) sui.sui_chunk_size = sn->sn_ChunkSize; sui.sui_num_chunks = sn->sn_Count; sui.sui_num_chunks_used = sn->sn_UseCount; + sui.sui_num_reused = sn->sn_NumReused; sui.sui_slab_index++; diff --git a/library/stdlib_malloc.c b/library/stdlib_malloc.c index 1c1987f..b0fdc71 100644 --- a/library/stdlib_malloc.c +++ b/library/stdlib_malloc.c @@ -503,7 +503,7 @@ STDLIB_CONSTRUCTOR(stdlib_memory_init) #if defined(__USE_SLAB_ALLOCATOR) { /* ZZZ this is just for the purpose of testing */ - #if 0 + #if 1 { TEXT slab_size_var[20]; diff --git a/library/stdlib_memory.h b/library/stdlib_memory.h index c27ef9a..2a22c20 100644 --- a/library/stdlib_memory.h +++ b/library/stdlib_memory.h @@ -235,6 +235,11 @@ struct SlabNode /* How many chunks of this slab are currently in use? */ ULONG sn_UseCount; + /* How many times was this slab reused instead of allocating + * it from system memory? + */ + ULONG sn_NumReused; + /* This contains all the chunks of memory which are available * for allocation. */ @@ -286,7 +291,7 @@ struct SlabData */ size_t sd_StandardSlabSize; - /* These fields kees track of how many entries there are in + /* These fields keep track of how many entries there are in * the sd_SingleAllocations list, and how much memory these * allocations occupy. */ @@ -303,6 +308,7 @@ struct SlabData extern struct SlabData NOCOMMON __slab_data; extern unsigned long NOCOMMON __slab_max_size; +extern unsigned long NOCOMMON __slab_purge_threshold; /****************************************************************************/ diff --git a/library/stdlib_realloc.c b/library/stdlib_realloc.c index 4461ad7..137ed8a 100644 --- a/library/stdlib_realloc.c +++ b/library/stdlib_realloc.c @@ -31,6 +31,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +/*#define DEBUG*/ + #ifndef _STDLIB_HEADERS_H #include "stdlib_headers.h" #endif /* _STDLIB_HEADERS_H */ diff --git a/library/stdlib_slab.c b/library/stdlib_slab.c index f0837bc..4548bfd 100644 --- a/library/stdlib_slab.c +++ b/library/stdlib_slab.c @@ -125,6 +125,7 @@ __slab_allocate(size_t allocation_size) else { struct MinList * slab_list = NULL; + BOOL slab_reused = FALSE; ULONG entry_size; ULONG chunk_size; int slab_index; @@ -252,11 +253,22 @@ __slab_allocate(size_t allocation_size) /* Unlink from list of empty slabs. */ Remove((struct Node *)free_node); - /* Unlink from list of slabs which keep chunks - * of the same size. It will be added there - * again, at a different position. + /* If the chunk size of the reused slab matches + * exactly what we need then we won't have to + * completely reinitialize it again. */ - Remove((struct Node *)sn); + if(sn->sn_ChunkSize == chunk_size) + { + slab_reused = TRUE; + } + else + { + /* Unlink from list of slabs which keep chunks + * of the same size. It will be added there + * again, at a different position. + */ + Remove((struct Node *)sn); + } D(("reusing a slab")); @@ -295,59 +307,75 @@ __slab_allocate(size_t allocation_size) if(new_sn != NULL) { - struct MinNode * free_chunk; - ULONG num_free_chunks = 0; - BYTE * first_byte; - BYTE * last_byte; - D(("setting up slab 0x%08lx", new_sn)); assert( chunk_size <= __slab_data.sd_StandardSlabSize ); - memset(new_sn,0,sizeof(*new_sn)); - - NewList((struct List *)&new_sn->sn_FreeList); - - /* Split up the slab memory into individual chunks - * of the same size and keep track of them - * in the free list. The memory managed by - * this slab immediately follows the - * SlabNode header. - */ - first_byte = (BYTE *)&new_sn[1]; - last_byte = &first_byte[__slab_data.sd_StandardSlabSize - chunk_size]; - - for(free_chunk = (struct MinNode *)first_byte ; - free_chunk <= (struct MinNode *)last_byte; - free_chunk = (struct MinNode *)(((BYTE *)free_chunk) + chunk_size)) + /* Do we have to completely initialize this slab from scratch? */ + if(NOT slab_reused) { - AddTail((struct List *)&new_sn->sn_FreeList, (struct Node *)free_chunk); - num_free_chunks++; - } + struct SlabChunk * free_chunk; + ULONG num_free_chunks = 0; + BYTE * first_byte; + BYTE * last_byte; - D(("slab contains %lu chunks, %lu bytes each",num_free_chunks,chunk_size)); + memset(new_sn,0,sizeof(*new_sn)); + + NewList((struct List *)&new_sn->sn_FreeList); + + /* This slab has room for new allocations, so make sure that + * it goes to the front of the slab list. It will be used + * by the next allocation request of this size. + */ + AddHead((struct List *)slab_list,(struct Node *)new_sn); + + /* Split up the slab memory into individual chunks + * of the same size and keep track of them + * in the free list. The memory managed by + * this slab immediately follows the + * SlabNode header. + */ + first_byte = (BYTE *)&new_sn[1]; + last_byte = &first_byte[__slab_data.sd_StandardSlabSize - chunk_size]; + + for(free_chunk = (struct SlabChunk *)first_byte ; + free_chunk <= (struct SlabChunk *)last_byte; + free_chunk = (struct SlabChunk *)(((BYTE *)free_chunk) + chunk_size)) + { + AddTail((struct List *)&new_sn->sn_FreeList, (struct Node *)free_chunk); + num_free_chunks++; + } + + new_sn->sn_Count = num_free_chunks; + new_sn->sn_ChunkSize = chunk_size; + + D(("new slab contains %lu chunks, %lu bytes each",num_free_chunks,chunk_size)); + } + /* This slab was reused and need not be reinitialized from scratch. */ + else + { + new_sn->sn_NumReused++; + + assert( new_sn->sn_FreeList.mlh_Head != NULL ); + assert( new_sn->sn_ChunkSize == chunk_size ); + assert( new_sn->sn_Count == 0 ); + } /* Grab the first free chunk (there has to be one). */ chunk = (struct SlabChunk *)RemHead((struct List *)&new_sn->sn_FreeList); - assert( chunk != NULL ); - /* Keep track of this chunk's parent slab. */ chunk->sc_Parent = new_sn; + assert( chunk != NULL ); + assert( chunk->sc_Parent == new_sn ); + allocation = &chunk[1]; - D(("allocation succeeded at 0x%08lx in slab 0x%08lx (slab use count = %lu)",allocation,new_sn,new_sn->sn_UseCount+1)); + /* This slab is now in use. */ + new_sn->sn_UseCount = 1; - /* Set up the new slab and put it where it belongs. */ - new_sn->sn_EmptyDecay = 0; - new_sn->sn_UseCount = 1; - new_sn->sn_Count = num_free_chunks; - new_sn->sn_ChunkSize = chunk_size; - - SHOWVALUE(new_sn->sn_ChunkSize); - - AddHead((struct List *)slab_list,(struct Node *)new_sn); + D(("allocation succeeded at 0x%08lx in slab 0x%08lx (slab use count = %lu)",allocation,new_sn,new_sn->sn_UseCount)); } /* Mark unused slabs for purging, and purge those which @@ -355,6 +383,8 @@ __slab_allocate(size_t allocation_size) */ if(purge) { + size_t total_purged = 0; + D(("purging empty slabs")); for(free_node = (struct MinNode *)__slab_data.sd_EmptySlabs.mlh_Head ; @@ -380,6 +410,15 @@ __slab_allocate(size_t allocation_size) Remove((struct Node *)sn); FreeVec(sn); + + total_purged += sizeof(*sn) + __slab_data.sd_StandardSlabSize; + + /* Stop releasing memory if we reach the threshold. If no + * threshold has been set, we will free as much memory + * as possible. + */ + if(__slab_purge_threshold > 0 && total_purged >= __slab_purge_threshold) + break; } /* Give it another chance. */ else @@ -700,6 +739,21 @@ __slab_init(size_t slab_size) /****************************************************************************/ +#if DEBUG + +static int print_json(void * ignore,const char * buffer,size_t len) +{ + extern void kputs(const char * str); + + kputs(buffer); + + return(0); +} + +#endif /* DEBUG */ + +/****************************************************************************/ + void __slab_exit(void) { @@ -712,7 +766,19 @@ __slab_exit(void) struct SlabNode * sn_next; struct MinNode * mn; struct MinNode * mn_next; - int i; + size_t slab_count = 0, total_slab_size = 0; + size_t single_allocation_count = 0, total_single_allocation_size = 0; + int i, j; + + #if DEBUG + { + kprintf("---BEGIN JSON DATA ---\n"); + + __get_slab_stats(NULL, print_json); + + kprintf("---END JSON DATA ---\n\n"); + } + #endif /* DEBUG */ D(("freeing slabs")); @@ -720,25 +786,38 @@ __slab_exit(void) for(i = 0 ; i < (int)NUM_ENTRIES(__slab_data.sd_Slabs) ; i++) { if(__slab_data.sd_Slabs[i].mlh_Head->mln_Succ != NULL) - D(("freeing slab #%ld (%lu bytes per chunk)", i, (1UL << i))); + D(("freeing slab slot #%ld (%lu bytes per chunk)", i, (1UL << i))); - for(sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head ; + for(sn = (struct SlabNode *)__slab_data.sd_Slabs[i].mlh_Head, j = 0 ; sn->sn_MinNode.mln_Succ != NULL ; sn = sn_next) { sn_next = (struct SlabNode *)sn->sn_MinNode.mln_Succ; + D((" slab #%ld.%ld at 0x%08lx",i, ++j, sn)); + D((" fragmentation = %ld%%",100 * (__slab_data.sd_StandardSlabSize - sn->sn_Count * sn->sn_ChunkSize) / __slab_data.sd_StandardSlabSize)); + D((" total space used = %ld (%ld%%)",sn->sn_UseCount * sn->sn_ChunkSize, 100 * sn->sn_UseCount / sn->sn_Count)); + D((" number of chunks total = %ld",sn->sn_Count)); + D((" number of chunks used = %ld%s",sn->sn_UseCount,sn->sn_UseCount == 0 ? " (empty)" : (sn->sn_UseCount == sn->sn_Count) ? " (full)" : "")); + D((" how often reused = %ld",sn->sn_NumReused)); + + total_slab_size += sizeof(*sn) + __slab_data.sd_StandardSlabSize; + slab_count++; + FreeVec(sn); } } + if(slab_count > 0) + D(("number of slabs = %ld, total slab size = %ld bytes",slab_count, total_slab_size)); + if(__slab_data.sd_SingleAllocations.mlh_Head->mln_Succ != NULL) D(("freeing single allocations")); /* Free the memory allocated for each allocation which did not * go into a slab. */ - for(mn = __slab_data.sd_SingleAllocations.mlh_Head ; + for(mn = __slab_data.sd_SingleAllocations.mlh_Head, j = 0 ; mn->mln_Succ != NULL ; mn = mn_next) { @@ -746,9 +825,17 @@ __slab_exit(void) ssa = (struct SlabSingleAllocation *)mn; + D((" allocation #%ld at 0x%08lx, %lu bytes", ++j, ssa, ssa->ssa_Size)); + + total_single_allocation_size += ssa->ssa_Size; + single_allocation_count++; + FreeMem(ssa, ssa->ssa_Size); } + if(single_allocation_count > 0) + D(("number of single allocations = %ld, total single allocation size = %ld", single_allocation_count, total_single_allocation_size)); + __slab_data.sd_InUse = FALSE; } diff --git a/library/stdlib_slab_purge_threshold.c b/library/stdlib_slab_purge_threshold.c new file mode 100644 index 0000000..d940429 --- /dev/null +++ b/library/stdlib_slab_purge_threshold.c @@ -0,0 +1,38 @@ +/* + * :ts=4 + * + * Portable ISO 'C' (1994) runtime library for the Amiga computer + * Copyright (c) 2002-2015 by Olaf Barthel + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Neither the name of Olaf Barthel nor the names of contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _STDLIB_HEADERS_H +#include "stdlib_headers.h" +#endif /* _STDLIB_HEADERS_H */ + +/****************************************************************************/ + +unsigned long __slab_purge_threshold; diff --git a/library/unix.lib_rev.h b/library/unix.lib_rev.h index 8b03422..7b80ead 100644 --- a/library/unix.lib_rev.h +++ b/library/unix.lib_rev.h @@ -1,6 +1,6 @@ #define VERSION 1 -#define REVISION 211 -#define DATE "23.11.2016" -#define VERS "unix.lib 1.211" -#define VSTRING "unix.lib 1.211 (23.11.2016)\r\n" -#define VERSTAG "\0$VER: unix.lib 1.211 (23.11.2016)" +#define REVISION 212 +#define DATE "27.11.2016" +#define VERS "unix.lib 1.212" +#define VSTRING "unix.lib 1.212 (27.11.2016)\r\n" +#define VERSTAG "\0$VER: unix.lib 1.212 (27.11.2016)" diff --git a/library/unix.lib_rev.rev b/library/unix.lib_rev.rev index dba40af..0d38910 100644 --- a/library/unix.lib_rev.rev +++ b/library/unix.lib_rev.rev @@ -1 +1 @@ -211 +212 diff --git a/test_programs/GNUmakefile.68k b/test_programs/GNUmakefile.68k index d8c0994..c93e45c 100644 --- a/test_programs/GNUmakefile.68k +++ b/test_programs/GNUmakefile.68k @@ -13,7 +13,7 @@ DELETE = delete all quiet .c.o: @echo "Compiling $<" - @$(CC) -c $(CFLAGS) $< + $(CC) -c $(CFLAGS) $< ############################################################################## @@ -31,7 +31,7 @@ WARNINGS = \ INCLUDE = -I../library/include LIB = -L../library/lib - OPTIONS = -DNDEBUG -fno-builtin -fwritable-strings -DNO_INLINE_STDARG -DIEEE_FLOATING_POINT_SUPPORT -DVERBOSE + OPTIONS = -DNDEBUG -fno-builtin -fwritable-strings -DNO_INLINE_STDARG -DIEEE_FLOATING_POINT_SUPPORT -DVERBOSE=1 #OPTIONS = -D__MEM_DEBUG -fno-builtin #OPTIONS = -DDEBUG -D__MEM_DEBUG -DNO_INLINE_STDARG -fno-builtin OPTIMIZE = -O @@ -50,14 +50,14 @@ all: test fgets_test iotest sscanf_test printf_test sprintf_test \ stack_size_test translate_test strtok_test uname simple \ fstat_stdout_test simple_sprintf date_test sscanf_64 factorial \ execvp_test setlocale rand fstat_test base_dir_nametest \ - malloc-test + malloc-test slab-test clean: $(DELETE) #?.o #?.map test fgets_test iotest sscanf_test printf_test \ sprintf_test stack_size_test translate_test strtok_test uname \ simple fstat_stdout_test fstat_test simple_sprintf date_test sscanf_64 \ factorial execvp_test setlocale rand base_dir_nametest \ - malloc-test + malloc-test slab-test ############################################################################## @@ -145,9 +145,13 @@ rand : rand.o @echo "Linking $@" $(CC) $(CFLAGS) -o $@ rand.o $(LIBS) -Wl,--cref,-M,-Map=$@.map -malloc-test: malloc-test.o +malloc-test : malloc-test.o @echo "Linking $@" - $(CC) $(CFLAGS) -o $@ rand.o $(LIBS) -Wl,--cref,-M,-Map=$@.map + $(CC) $(CFLAGS) -o $@ malloc-test.o $(LIBS) -Wl,--cref,-M,-Map=$@.map + +slab-test : slab-test.o + @echo "Linking $@" + $(CC) $(CFLAGS) -o $@ slab-test.o $(LIBS) -Wl,--cref,-M,-Map=$@.map ############################################################################## diff --git a/test_programs/slab-test.c b/test_programs/slab-test.c new file mode 100644 index 0000000..980151f --- /dev/null +++ b/test_programs/slab-test.c @@ -0,0 +1,26 @@ +#include +#include + +unsigned long __slab_max_size = 4096; + +static int print_json(void * ignore,const char * buffer,size_t len) +{ + fputs(buffer, stdout); + + return(0); +} + +int +main(int argc,char ** argv) +{ + int i; + + srand(1); + + for(i = 0 ; i < 1000 ; i++) + malloc(1 + (rand() % 8192)); + + __get_slab_stats(NULL, print_json); + + return(0); +}