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); +}