Hi there, After experiencing MAX_OBJECTS-related excessive swapping on Invidious that allowed Saromok's souped-up goblins to poison and kill my character, I've decided to take another look at memory management.
I may be missing some historical context for why things are the way they currently are in Crossfire, so I welcome feedback on what these proposed changes might do or not do. The rationale for each change is included in each patch, but reproduced here for convenience: Subject: [PATCH 1/2] Retire MAX_OBJECTS MAX_OBJECTS has probably outlived its usefulness. It was previously "no hard limit," only serving to trigger map swapping immediately after loading a map, if the number of used objects exceeded MAX_OBJECTS. At worse case, MAX_OBJECTS causes an O(n^2) search and O(n) swaps, where n is the number of maps in memory. This happens immediately after loading a very large map. The server takes O(n) to search for the map with the smallest remaining timeout and swaps it, and does this n times or until enough memory is freed. If the other maps are small, this does not free much memory and causes the "performance hit" mentioned in the comments. This was observed on Invidious, where the server experienced delays of up to 700 ms immediately after loading a large map due to excessive swapping. Removing MAX_OBJECTS does not significantly change the server's allocation pattern because the server never frees memory (it maintains an internal free list) and because maps are swapped out based on timeout at the end of each tick anyway. Subject: [PATCH 2/2] Retire object allocator The object allocator allocates OBJ_EXPAND objects at a time and manages its own free list. It is faster at allocating many objects at once, but defeats memory debuggers and mitigation techniques. It also does not return unused memory to the operating system. Rewrite object_new() to always allocate memory using calloc(). This will incur some memory overhead since an object struct does not fit perfectly into a nice allocator slab size. object_free2() adds freed objects to a free list that is reclaimed at the end of each server tick. It cannot immediately free object memory, because some callers expect to be able to query it for FLAG_FREED to detect object removal. These changes were tested locally (mostly by running a character around) with 1) clang -fsanitize=address, which did not report any errors, and 2) with Jemalloc statistics enabled (see attached, jemalloc.out). As expected, after the change, the allocator bin seeing the most activity was 768 bytes (struct object is 656 bytes on x86-64). -- Kevin Zheng kevinz5...@gmail.com | kev...@berkeley.edu XMPP: kev...@eecs.berkeley.edu
From fe2480277fa6e78db591fac3787a5779cb4a01f4 Mon Sep 17 00:00:00 2001 From: Kevin Zheng <kevinz5...@gmail.com> Date: Mon, 9 Mar 2020 09:54:01 -0700 Subject: [PATCH 1/2] Retire MAX_OBJECTS MAX_OBJECTS has probably outlived its usefulness. It was previously "no hard limit," only serving to trigger map swapping immediately after loading a map, if the number of used objects exceeded MAX_OBJECTS. At worse case, MAX_OBJECTS causes an O(n^2) search and O(n) swaps, where n is the number of maps in memory. This happens immediately after loading a very large map. The server takes O(n) to search for the map with the smallest remaining timeout and swaps it, and does this n times or until enough memory is freed. If the other maps are small, this does not free much memory and causes the "performance hit" mentioned in the comments. This was observed on Invidious, where the server experienced delays of up to 700 ms immediately after loading a large map due to excessive swapping. Removing MAX_OBJECTS does not significantly change the server's allocation pattern because the server never frees memory (it maintains an internal free list) and because maps are swapped out based on timeout at the end of each tick anyway. --- include/config.h | 41 ------------------------------------- include/sproto.h | 1 - server/c_misc.c | 3 +-- server/monster.c | 2 +- server/server.c | 1 - server/swap.c | 53 ------------------------------------------------ 6 files changed, 2 insertions(+), 99 deletions(-) diff --git a/include/config.h b/include/config.h index 4eb2ef92..7752c22a 100644 --- a/include/config.h +++ b/include/config.h @@ -336,8 +336,6 @@ * DMFILE - file with dm/wizard access lists * LOGFILE - where to log if using -daemon option * MAP_ - various map timeout and swapping parameters - * MAX_OBJECTS - how many objects to keep in memory. - * MAX_OBJECTS_LWM - only swap maps out if below that value * MOTD - message of the day - printed each time someone joins the game * PERM_FILE - limit play times * SHUTDOWN - used when shutting down the server @@ -427,45 +425,6 @@ /** Default time to reset. */ #define MAP_DEFAULTRESET 7200 -/** - * MAX_OBJECTS is no hard limit. If this limit is exceeded, Crossfire - * will look for maps which are already scheldued for swapping, and - * promptly swap them out before new maps are being loaded. - * If playing only by yourself, this number can probably be as low as - * 3000. If in server mode, probably figure about 1000-2000 objects per - * active player (if they typically play on different maps), for some guess - * on how many to define. If it is too low, maps just get swapped out - * immediately, causing a performance hit. If it is too high, the program - * consumes more memory. If you have gobs of free memory, a high number - * might not be a bad idea. Each object is around 656 bytes right now. - * 100000 is about 63 MiB - */ -#define MAX_OBJECTS 100000 - -/** - * Max objects low water mark (lwm). If defined, the map swapping strategy - * is a bit different: - * 1) We only start swapping maps if the number of objects in use is - * greater than MAX_OBJECTS above. - * 2) We keep swapping maps until there are no more maps to swap or the number - * of used objects drop below this low water mark value. - * - * If this is not defined, maps are swapped out on the timeout value above, - * or if the number of objects used is greater than MAX_OBJECTS above. - * - * Note: While this will prevent the pauses noticed when saving maps, there - * can instead be cpu performance penalties - any objects in memory get - * processed. So if there are 4000 objects in memory, and 1000 of them - * are living objects, the system will process all 1000 objects each tick. - * With swapping enable, maybe 600 of the objects would have gotten swapped - * out. This is less likely a problem with a smaller number of MAX_OBJECTS - * than if it is very large. - * Also, the pauses you do get can be worse, as if you enter a map with - * a lot of new objects and go above MAX_OBJECTS, it may have to swap out - * many maps to get below the low water mark. - */ -/*#define MAX_OBJECTS_LWM MAX_OBJECTS/2*/ - /** * Defining MEMORY_DEBUG disables Crossfire's object allocator, which allocates * OBJ_EXPAND objects at a time and manages its own free list. It is faster at diff --git a/include/sproto.h b/include/sproto.h index 5aacf3c8..6eea26c1 100644 --- a/include/sproto.h +++ b/include/sproto.h @@ -581,7 +581,6 @@ void rod_adjust(object *rod); void read_map_log(void); int swap_map(mapstruct *map); void check_active_maps(void); -void swap_below_max(const char *except_level); int players_on_map(mapstruct *m, int show_all); void flush_old_maps(void); /* time.c */ diff --git a/server/c_misc.c b/server/c_misc.c index a117f8cb..130f5cda 100644 --- a/server/c_misc.c +++ b/server/c_misc.c @@ -237,8 +237,7 @@ static void malloc_info(object *op) { i18n(op, "[fixed]Objects:")); draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MALLOC, - i18n(op, "[fixed]%6d used (%.2f%% of %d max)"), - ob_used, (float)ob_used / MAX_OBJECTS * 100, MAX_OBJECTS); + i18n(op, "[fixed]%6d used"), ob_used); if (ob_used != nrofallocobjects - nroffreeobjects) { draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MALLOC, diff --git a/server/monster.c b/server/monster.c index 31a380e5..0ba4fab0 100644 --- a/server/monster.c +++ b/server/monster.c @@ -2207,7 +2207,7 @@ void monster_communicate(object *op, const char *txt) { FOR_MAP_PREPARE(mp, x, y, npc) { monster_talk_to_npc(npc, &info); if (orig_map != op->map) { - LOG(llevDebug, "Warning: Forced to swap out very recent map - MAX_OBJECTS should probably be increased\n"); + LOG(llevDebug, "Warning: Forced to swap out very recent map\n"); return; } } FOR_MAP_FINISH(); diff --git a/server/server.c b/server/server.c index 7b15f3a1..89b6bdfc 100644 --- a/server/server.c +++ b/server/server.c @@ -264,7 +264,6 @@ static void enter_map(object *op, mapstruct *newmap, int x, int y) { set_map_timeout(oldmap); } } - swap_below_max(newmap->path); if (op->type == PLAYER) { map_newmap_cmd(&op->contr->socket); diff --git a/server/swap.c b/server/swap.c index a9ed0e2e..7c1b130e 100644 --- a/server/swap.c +++ b/server/swap.c @@ -195,66 +195,13 @@ void check_active_maps(void) { next = map->next; if (map->in_memory == MAP_IN_MEMORY && map->timeout != 0) { map->timeout -= 1; - /* If LWM is set, we only swap maps out when we run out of objects */ -#ifndef MAX_OBJECTS_LWM if (map->timeout == 0) { swap_map(map); } -#endif } } } -/** - * Returns the map with the lowest timeout variable (not 0). - * - * @param except_level - * path of map to ignore for reset. Musn't be NULL. - * @return - * map, or NULL if no map is ready for reset. - */ -static mapstruct *map_least_timeout(const char *except_level) { - mapstruct *map, *chosen = NULL; - int timeout = MAP_MAXTIMEOUT+1; - - for (map = first_map; map != NULL; map = map->next) - if (map->in_memory == MAP_IN_MEMORY - && strcmp(map->path, except_level) - && map->timeout - && map->timeout < timeout) - chosen = map, timeout = map->timeout; - return chosen; -} - -/** - * Tries to swap out maps which are still in memory, because - * of MAP_TIMEOUT until used objects is below MAX_OBJECTS or there are - * no more maps to swap. - * - * @param except_level - * path of map to ignore for reset. Musn't be NULL. - */ -void swap_below_max(const char *except_level) { - mapstruct *map; - - if (nrofallocobjects-nroffreeobjects < MAX_OBJECTS) - return; - for (;;) { -#ifdef MAX_OBJECTS_LWM - if (nrofallocobjects-nroffreeobjects < MAX_OBJECTS_LWM) - return; -#else - if (nrofallocobjects-nroffreeobjects < MAX_OBJECTS) - return; -#endif - if ((map = map_least_timeout(except_level)) == NULL) - return; - LOG(llevDebug, "Trying to swap out %s before its time.\n", map->path); - map->timeout = 0; - swap_map(map); - } -} - /** * Returns the count of players on a map, calculated from player list. * -- 2.24.0
From 9fed1a2527c9fb400f95fe65cf49988a0b68d62e Mon Sep 17 00:00:00 2001 From: Kevin Zheng <kevinz5...@gmail.com> Date: Mon, 9 Mar 2020 11:22:58 -0700 Subject: [PATCH 2/2] Retire object allocator The object allocator allocates OBJ_EXPAND objects at a time and manages its own free list. It is faster at allocating many objects at once, but defeats memory debuggers and mitigation techniques. It also does not return unused memory to the operating system. Rewrite object_new() to always allocate memory using calloc(). This will incur some memory overhead since an object struct does not fit perfectly into a nice allocator slab size. object_free2() adds freed objects to a free list that is reclaimed at the end of each server tick. It cannot immediately free object memory, because some callers expect to be able to query it for FLAG_FREED to detect object removal. --- common/object.c | 133 ++++++++--------------------------------------- include/config.h | 15 ------ include/object.h | 4 +- server/c_misc.c | 19 +------ server/server.c | 1 + 5 files changed, 29 insertions(+), 143 deletions(-) diff --git a/common/object.c b/common/object.c index dab30909..7a465eb7 100644 --- a/common/object.c +++ b/common/object.c @@ -41,21 +41,12 @@ static int compare_ob_value_lists_one(const object *, const object *); static int compare_ob_value_lists(const object *, const object *); -static void expand_objects(void); static void permute(int *, int, int); static int object_set_value_s(object *, const char *, const char *, int); static void object_increase_nrof(object *op, uint32_t i); -#ifdef MEMORY_DEBUG -int nroffreeobjects = 0; /**< Number of free objects. */ int nrofallocobjects = 0; /**< Number of allocated objects. */ -#undef OBJ_EXPAND -#define OBJ_EXPAND 1 -#else -static object objarray[STARTMAX]; /**< All objects, allocated this way at first */ -int nroffreeobjects = STARTMAX; /**< How many OBs allocated and free (free) */ -int nrofallocobjects = STARTMAX; /**< How many OBs allocated (free + used) */ -#endif +int nrofallocobjects_peak = 0; //< Peak number of allocated objects object *objects; /**< Pointer to the list of used objects */ static object *free_objects; /**< Pointer to the list of unused objects */ @@ -94,26 +85,7 @@ void init_objects(void) { /* Initialize all objects: */ objects = NULL; active_objects = NULL; - -#ifdef MEMORY_DEBUG free_objects = NULL; -#else - free_objects = objarray; - objarray[0].prev = NULL, - objarray[0].next = &objarray[1], - SET_FLAG(&objarray[0], FLAG_REMOVED); - SET_FLAG(&objarray[0], FLAG_FREED); - for (int i = 1; i < STARTMAX-1; i++) { - objarray[i].next = &objarray[i+1]; - objarray[i].prev = &objarray[i-1]; - SET_FLAG(&objarray[i], FLAG_REMOVED); - SET_FLAG(&objarray[i], FLAG_FREED); - } - objarray[STARTMAX-1].next = NULL; - objarray[STARTMAX-1].prev = &objarray[STARTMAX-2]; - SET_FLAG(&objarray[STARTMAX-1], FLAG_REMOVED); - SET_FLAG(&objarray[STARTMAX-1], FLAG_FREED); -#endif } /** @@ -538,6 +510,17 @@ object *object_find_by_name_global(const char *str) { return op; } +void object_free_freelist(void) { + object *op, *next; + for (op = free_objects; op != NULL; ) { + next = op->next; + free(op); + nrofallocobjects--; + op = next; + } + free_objects = NULL; +} + /** * Destroys all allocated objects. * @@ -551,18 +534,8 @@ object *object_find_by_name_global(const char *str) { * free_all_object_data() has been renamed to object_free_object_data() */ void object_free_all_data(void) { -#ifdef MEMORY_DEBUG + object_free_freelist(); object *op, *next; - - for (op = free_objects; op != NULL; ) { - next = op->next; - free(op); - nrofallocobjects--; - nroffreeobjects--; - op = next; - } - free_objects = NULL; - for (op = objects; op != NULL; ) { next = op->next; if (!QUERY_FLAG(op, FLAG_FREED)) { @@ -570,9 +543,8 @@ void object_free_all_data(void) { } op = next; } -#endif - LOG(llevDebug, "%d allocated objects, %d free objects, STARMAX=%d\n", nrofallocobjects, nroffreeobjects, STARTMAX); + LOG(llevDebug, "%d allocated objects\n", nrofallocobjects); } /** @@ -1015,83 +987,27 @@ void object_copy_with_inv(const object *src_ob, object *dest_ob) { } /** - * Allocates more objects for the list of unused objects. - * - * It is called from object_new() if the unused list is empty. - * - * If there is not enough memory, fatal() is called. - */ -static void expand_objects(void) { - int i; - object *new; - - new = (object *)CALLOC(OBJ_EXPAND, sizeof(object)); - - if (new == NULL) - fatal(OUT_OF_MEMORY); - free_objects = new; - new[0].prev = NULL; - new[0].next = &new[1], - SET_FLAG(&new[0], FLAG_REMOVED); - SET_FLAG(&new[0], FLAG_FREED); - - for (i = 1; i < OBJ_EXPAND-1; i++) { - new[i].next = &new[i+1], - new[i].prev = &new[i-1], - SET_FLAG(&new[i], FLAG_REMOVED); - SET_FLAG(&new[i], FLAG_FREED); - } - new[OBJ_EXPAND-1].prev = &new[OBJ_EXPAND-2], - new[OBJ_EXPAND-1].next = NULL, - SET_FLAG(&new[OBJ_EXPAND-1], FLAG_REMOVED); - SET_FLAG(&new[OBJ_EXPAND-1], FLAG_FREED); - - nrofallocobjects += OBJ_EXPAND; - nroffreeobjects += OBJ_EXPAND; -} - -/** - * Grabs an object from the list of unused objects, makes - * sure it is initialised, and returns it. - * - * If there are no free objects, expand_objects() is called to get more. + * Allocate an object, makes sure it is initialised, and returns it. * * @return * cleared and ready to use object*. * * @note - * will never fail, as expand_objects() will fatal() if memory allocation error. + * will never fail, as will fatal() if memory allocation error. * * @note * get_object() has been renamed to object_new() */ object *object_new(void) { object *op; -#ifdef MEMORY_DEBUG - /* FIXME: However this doesn't work since object_free2() sometimes add - * objects back to the free_objects linked list, and some functions mess - * with the object after return of object_free2(). This is bad and should be - * fixed. But it would need fairly extensive changes and a lot of debugging. - */ op = calloc(1, sizeof(object)); - if (op == NULL) + if (op == NULL) { fatal(OUT_OF_MEMORY); -#else - if (free_objects == NULL) { - expand_objects(); } - op = free_objects; - if (!QUERY_FLAG(op, FLAG_FREED)) { - LOG(llevError, "Fatal: Getting busy object.\n"); -#ifdef MANY_CORES - abort(); -#endif + nrofallocobjects++; + if (nrofallocobjects > nrofallocobjects_peak) { + nrofallocobjects_peak = nrofallocobjects; } - free_objects = op->next; - if (free_objects != NULL) - free_objects->prev = NULL; - nroffreeobjects--; -#endif op->count = ++ob_count; op->name = NULL; op->name_pl = NULL; @@ -1550,17 +1466,14 @@ void object_free2(object *ob, int flags) { } } -#ifdef MEMORY_DEBUG - free(ob); -#else - /* Now link it with the free_objects list: */ + /* Now link it with the free_objects list for reclaimation at end of tick. + * This allows the server to briefly use the object after "free" to check + * for FLAG_REMOVED */ ob->prev = NULL; ob->next = free_objects; if (free_objects != NULL) free_objects->prev = ob; free_objects = ob; - nroffreeobjects++; -#endif } /** diff --git a/include/config.h b/include/config.h index 7752c22a..3461308f 100644 --- a/include/config.h +++ b/include/config.h @@ -425,19 +425,6 @@ /** Default time to reset. */ #define MAP_DEFAULTRESET 7200 -/** - * Defining MEMORY_DEBUG disables Crossfire's object allocator, which allocates - * OBJ_EXPAND objects at a time and manages its own free list. It is faster at - * allocating lots of objects, but defeats memory debuggers and mitigation - * techniques by managing its own allocations and enabling use-after-free bugs. - * - * Unfortunately, much of the code does not work without use-after-free because - * even checking if an object was freed requires looking in the object. Until - * these issues are fixed, Crossfire needs MEMORY_DEBUG unset in order to run - * without crashing frequently. - */ -/*#define MEMORY_DEBUG*/ - /** * If you want to have a Message Of The Day file, define MOTD to be * the file with the message. If the file doesn't exist or if it @@ -520,8 +507,6 @@ #define BANISHFILE "banish_file" #define MAX_ERRORS 25 /**< Bail out if more are received during tick. */ -#define STARTMAX 500 /**< How big array of objects to start with. */ -#define OBJ_EXPAND 100 /**< How big steps to use when expanding array. */ #define HIGHSCORE_LENGTH 1000 /**< How many entries there are room for. */ diff --git a/include/object.h b/include/object.h index a03b0206..9c4fa999 100644 --- a/include/object.h +++ b/include/object.h @@ -477,7 +477,7 @@ extern object *objects; extern object *active_objects; extern int nrofallocobjects; -extern int nroffreeobjects; +extern int nrofallocobjects_peak; static inline int compare_flags(const object *p, const object *q) { return ((p)->flags[0] == (q)->flags[0]) && @@ -601,4 +601,6 @@ static inline bool CAN_PROBE(const object *ob) { (ob->type == PLAYER || QUERY_FLAG(ob, FLAG_MONSTER)); } +void object_free_freelist(void); + #endif /* OBJECT_H */ diff --git a/server/c_misc.c b/server/c_misc.c index 130f5cda..37eba354 100644 --- a/server/c_misc.c +++ b/server/c_misc.c @@ -237,23 +237,8 @@ static void malloc_info(object *op) { i18n(op, "[fixed]Objects:")); draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MALLOC, - i18n(op, "[fixed]%6d used"), ob_used); - - if (ob_used != nrofallocobjects - nroffreeobjects) { - draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MALLOC, - i18n(op, "[fixed] (used list mismatch: %d)"), - nrofallocobjects - nroffreeobjects); - } - - draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MALLOC, - i18n(op, "[fixed]%6d free (%.2f%% of %d allocated)"), - ob_free, (float)ob_free / nrofallocobjects * 100, nrofallocobjects); - - if (ob_free != nroffreeobjects) { - draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MALLOC, - i18n(op, "[fixed] (free list mismatch: %d)"), - nroffreeobjects); - } + i18n(op, "[fixed]%6d used (%d peak), %d laundry (of %d allocated)"), + ob_used, nrofallocobjects_peak, ob_free, nrofallocobjects); draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_MALLOC, i18n(op, "[fixed]%6d on active list"), diff --git a/server/server.c b/server/server.c index 89b6bdfc..efa9fee6 100644 --- a/server/server.c +++ b/server/server.c @@ -1456,6 +1456,7 @@ void server_main(int argc, char *argv[]) { check_active_maps(); /* Removes unused maps after a certain timeout */ do_specials(); /* Routines called from time to time. */ update_players(); + object_free_freelist(); } /* This is unreachable. */ -- 2.24.0
___ Begin jemalloc statistics ___ Version: "5.1.0-0-g61efbda7098de6fe64c362d309824864308c36d4" Build-time option settings config.cache_oblivious: true config.debug: false config.fill: true config.lazy_lock: true config.malloc_conf: "abort_conf:false" config.prof: false config.prof_libgcc: false config.prof_libunwind: false config.stats: true config.utrace: true config.xmalloc: true Run-time option settings opt.abort: false opt.abort_conf: false opt.retain: false opt.dss: "secondary" opt.narenas: 16 opt.percpu_arena: "disabled" opt.metadata_thp: "disabled" opt.dirty_decay_ms: 10000 (arenas.dirty_decay_ms: 10000) opt.muzzy_decay_ms: 10000 (arenas.muzzy_decay_ms: 10000) opt.junk: "false" opt.zero: false opt.utrace: false opt.xmalloc: false opt.tcache: true opt.lg_tcache_max: 15 opt.thp: "not supported" opt.stats_print: true opt.stats_print_opts: "" Arenas: 16 Quantum size: 16 Page size: 4096 Maximum thread-cached size class: 32768 Number of bin size classes: 36 Number of thread-cache bin size classes: 41 Number of large size classes: 196 Allocated: 15677408, active: 17059840, metadata: 3074680 (n_thp 0), resident: 36777984, mapped: 40017920, retained: 0 Background threads: 0, num_runs: 0, run_interval: 0 ns n_lock_ops n_waiting n_spin_acq n_owner_switch total_wait_ns max_wait_ns max_n_thds background_thread 0 0 0 0 0 0 0 ctl 0 0 0 0 0 0 0 prof 0 0 0 0 0 0 0 arenas[0]: assigned threads: 1 uptime: 173942476214 dss allocation precedence: "disabled" decaying: time npages sweeps madvises purged dirty: 10000 3987 34 590 8930 muzzy: 10000 82 29 440 6816 allocated nmalloc ndalloc nrequests small: 14211040 290027 244418 488249 large: 1466368 136 122 136 total: 15677408 290163 244540 488385 active: 17059840 mapped: 40017920 retained: 0 base: 3046000 internal: 28680 metadata_thp: 0 tcache_bytes: 370512 resident: 36777984 n_lock_ops n_waiting n_spin_acq n_owner_switch total_wait_ns max_wait_ns max_n_thds large 0 0 0 0 0 0 0 extent_avail 0 0 0 0 0 0 0 extents_dirty 0 0 0 0 0 0 0 extents_muzzy 0 0 0 0 0 0 0 extents_retained 0 0 0 0 0 0 0 decay_dirty 0 0 0 0 0 0 0 decay_muzzy 0 0 0 0 0 0 0 base 0 0 0 0 0 0 0 tcache_list 0 0 0 0 0 0 0 bins: size ind allocated nmalloc ndalloc nrequests curregs curslabs regs pgs util nfills nflushes nslabs nreslabs n_lock_ops n_waiting n_spin_acq n_owner_switch total_wait_ns max_wait_ns max_n_thds 8 0 13888 1832 96 3344 1736 4 512 1 0.847 74 21 4 0 0 0 0 0 0 0 0 16 1 112736 7226 180 9847 7046 28 256 1 0.982 149 39 28 0 0 0 0 0 0 0 0 32 2 131840 112502 108382 202364 4120 58 128 1 0.554 8325 1112 760 202 0 0 0 0 0 0 0 48 3 378336 34220 26338 36015 7882 58 256 3 0.530 4948 299 78 219 0 0 0 0 0 0 0 64 4 163264 4907 2356 8682 2551 42 64 1 0.949 174 81 73 14 0 0 0 0 0 0 0 80 5 241920 3226 202 6190 3024 12 256 5 0.984 66 40 13 1 0 0 0 0 0 0 0 96 6 42240 554 114 2767 440 4 128 3 0.859 18 24 5 1 0 0 0 0 0 0 0 112 7 6048 190 136 83 54 1 256 7 0.210 9 15 1 0 0 0 0 0 0 0 0 128 8 14080 158 48 201 110 4 32 1 0.859 15 14 5 1 0 0 0 0 0 0 0 160 9 127680 954 156 882 798 7 128 5 0.890 38 17 7 0 0 0 0 0 0 0 0 192 10 232704 1300 88 1233 1212 19 64 3 0.996 30 9 21 2 0 0 0 0 0 0 0 224 11 213696 1086 132 1033 954 8 128 7 0.931 40 14 8 0 0 0 0 0 0 0 0 256 12 196096 824 58 83864 766 48 16 1 0.997 100 29 52 5 0 0 0 0 0 0 0 320 13 358720 1200 79 1179 1121 18 64 5 0.973 36 11 18 0 0 0 0 0 0 0 0 384 14 361344 1020 79 992 941 30 32 3 0.980 57 7 31 1 0 0 0 0 0 0 0 448 15 254912 698 129 623 569 9 64 7 0.987 40 11 11 2 0 0 0 0 0 0 0 512 16 172032 349 13 393 336 43 8 1 0.976 38 5 44 6 0 0 0 0 0 0 0 640 17 275200 631 201 4217 430 15 32 5 0.895 47 33 19 4 0 0 0 0 0 0 0 768 18 7500288 114852 105086 117075 9766 656 16 3 0.930 19728 6589 6811 1177 0 0 0 0 0 0 0 896 19 159488 236 58 223 178 6 32 7 0.927 29 14 6 0 0 0 0 0 0 0 0 1024 20 440320 480 50 484 430 108 4 1 0.995 145 29 121 7 0 0 0 0 0 0 0 1280 21 398080 393 82 3796 311 21 16 5 0.925 115 35 22 4 0 0 0 0 0 0 0 1536 22 296448 215 22 223 193 25 8 3 0.965 53 7 27 2 0 0 0 0 0 0 0 1792 23 250880 166 26 158 140 9 16 7 0.972 23 10 9 0 0 0 0 0 0 0 0 2048 24 165888 107 26 97 81 41 2 1 0.987 45 8 54 2 0 0 0 0 0 0 0 2560 25 258560 119 18 114 101 13 8 5 0.971 56 7 16 1 0 0 0 0 0 0 0 3072 26 479232 194 38 210 156 44 4 3 0.886 71 8 49 5 0 0 0 0 0 0 0 3584 27 60928 57 40 1569 17 3 8 7 0.708 32 27 5 0 0 0 0 0 0 0 0 4096 28 135168 70 37 181 33 33 1 1 1 20 17 70 0 0 0 0 0 0 0 0 5120 29 322560 105 42 71 63 16 4 5 0.984 22 18 25 4 0 0 0 0 0 0 0 6144 30 104448 31 14 25 17 9 2 3 0.944 6 6 15 3 0 0 0 0 0 0 0 7168 31 43008 28 22 18 6 2 4 7 0.750 9 9 9 3 0 0 0 0 0 0 0 8192 32 57344 20 13 19 7 7 1 2 1 6 5 20 0 0 0 0 0 0 0 0 10240 33 20480 19 17 30 2 1 2 5 1 4 5 10 0 0 0 0 0 0 0 0 12288 34 221184 42 24 33 18 18 1 3 1 8 7 42 0 0 0 0 0 0 0 0 14336 35 0 16 16 14 0 0 2 7 1 6 7 9 0 0 0 0 0 0 0 0 large: size ind allocated nmalloc ndalloc nrequests curlextents 16384 36 16384 4 3 9 1 20480 37 20480 6 5 61 1 24576 38 49152 4 2 5 2 28672 39 86016 7 4 10 3 --- 40960 41 0 19 19 19 0 49152 42 0 10 10 10 0 57344 43 0 3 3 3 0 65536 44 0 3 3 3 0 81920 45 0 4 4 4 0 98304 46 98304 5 4 5 1 114688 47 344064 19 16 19 3 131072 48 0 22 22 22 0 --- 196608 50 196608 3 2 3 1 229376 51 0 1 1 1 0 --- 327680 53 655360 25 23 25 2 --- 458752 55 0 1 1 1 0 --- --- End jemalloc statistics ---
_______________________________________________ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire