diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b2ac96eb9..518bdd9c0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,10 +20,13 @@ set(rscore_srcs log/stats.c lp/lp.c lp/process.c - mm/auto_ckpt.c mm/buddy/buddy.c mm/buddy/ckpt.c - mm/buddy/multi.c + mm/buddy/multi_buddy.c + mm/dymelor/checkpoint.c + mm/dymelor/dymelor.c + mm/auto_ckpt.c + mm/model_allocator.c mm/msg_allocator.c parallel/parallel.c serial/serial.c) diff --git a/src/ROOT-Sim.h b/src/ROOT-Sim.h index 617cb3203..2d0e1a308 100644 --- a/src/ROOT-Sim.h +++ b/src/ROOT-Sim.h @@ -206,6 +206,11 @@ extern struct topology *vInitializeTopology(enum topology_geometry geometry, int #define InitializeTopology(geometry, ...) vInitializeTopology(geometry, PP_NARG(__VA_ARGS__), __VA_ARGS__) /********* TOPOLOGY LIBRARY ************/ +enum mm_allocator_choice { + MM_MULTI_BUDDY, + MM_DYMELOR +}; + /// A set of configurable values used by other modules struct simulation_configuration { /// The number of LPs to be used in the simulation @@ -214,6 +219,8 @@ struct simulation_configuration { unsigned n_threads; /// The target termination logical time. Setting this value to zero means that LVT-based termination is disabled simtime_t termination_time; + /// The allocator to use for model's memory + enum mm_allocator_choice mm; /// The gvt period expressed in microseconds unsigned gvt_period; /// The logger verbosity level diff --git a/src/datatypes/bitmap.h b/src/datatypes/bitmap.h index ae180d17c..fb98c1aa1 100644 --- a/src/datatypes/bitmap.h +++ b/src/datatypes/bitmap.h @@ -117,8 +117,8 @@ typedef unsigned char block_bitmap; */ #define bitmap_count_set(bitmap, bitmap_size) \ __extension__({ \ - unsigned __i = (bitmap_size) / B_BLOCK_SIZE; \ - unsigned __ret = 0; \ + uint_least32_t __i = (bitmap_size) / B_BLOCK_SIZE; \ + uint_least32_t __ret = 0; \ B_BLOCK_TYPE *__block_b = B_UNION_CAST(bitmap); \ while(__i--) { \ __ret += intrinsics_popcount(__block_b[__i]); \ @@ -149,8 +149,8 @@ typedef unsigned char block_bitmap; */ #define bitmap_first_reset(bitmap, bitmap_size) \ __extension__({ \ - unsigned __i, __blocks = (bitmap_size) / B_BLOCK_SIZE; \ - unsigned __ret = UINT_MAX; \ + uint_least32_t __i, __blocks = (bitmap_size) / B_BLOCK_SIZE; \ + uint_least32_t __ret = UINT_MAX; \ B_BLOCK_TYPE __cur_block, *__block_b = B_UNION_CAST(bitmap); \ for(__i = 0; __i < __blocks; ++__i) { \ if((__cur_block = ~__block_b[__i])) { \ @@ -172,7 +172,7 @@ typedef unsigned char block_bitmap; */ #define bitmap_foreach_set(bitmap, bitmap_size, func) \ __extension__({ \ - unsigned __i, __fnd, __blocks = (bitmap_size) / B_BLOCK_SIZE; \ + uint_least32_t __i, __fnd, __blocks = (bitmap_size) / B_BLOCK_SIZE; \ B_BLOCK_TYPE __cur_block, *__block_b = B_UNION_CAST(bitmap); \ for(__i = 0; __i < __blocks; ++__i) { \ if((__cur_block = __block_b[__i])) { \ diff --git a/src/init.c b/src/init.c index 817e206f4..8b12a033c 100644 --- a/src/init.c +++ b/src/init.c @@ -102,6 +102,11 @@ int RootsimInit(const struct simulation_configuration *conf) return -1; } + if(unlikely(global_config.mm != MM_MULTI_BUDDY && global_config.mm != MM_DYMELOR)) { + fprintf(stderr, "Incorrect model memory allocator choice\n"); + return -1; + } + if(unlikely(global_config.n_threads > thread_cores_count())) { fprintf(stderr, "Demanding %u cores, which are more than available (%u)\n", global_config.n_threads, thread_cores_count()); diff --git a/src/lp/lp.c b/src/lp/lp.c index 2a4ff3f28..1100a45bb 100644 --- a/src/lp/lp.c +++ b/src/lp/lp.c @@ -93,7 +93,7 @@ void lp_init(void) struct lp_ctx *lp = &lps[i]; current_lp = lp; - model_allocator_lp_init(); + model_allocator_lp_init(&lp->mm_state); lp->state_pointer = NULL; lp->fossil_epoch = 0; lp->rng_ctx = rs_malloc(sizeof(*lp->rng_ctx)); @@ -121,11 +121,12 @@ void lp_fini(void) sync_thread_barrier(); for(uint64_t i = lid_thread_first; i < lid_thread_end; ++i) { - current_lp = &lps[i]; + struct lp_ctx *lp = &lps[i]; + current_lp = lp; process_lp_fini(); msg_queue_lp_fini(); - model_allocator_lp_fini(); + model_allocator_lp_fini(&lp->mm_state); } current_lp = NULL; diff --git a/src/lp/process.c b/src/lp/process.c index 49aaf01d3..a9751d856 100644 --- a/src/lp/process.c +++ b/src/lp/process.c @@ -68,21 +68,6 @@ void ScheduleNewEvent(lp_id_t receiver, simtime_t timestamp, unsigned event_type } } -/** - * @brief Take a checkpoint of the state of a LP - * @param lp the LP to checkpoint - * - * The actual checkpoint operation is delegated to the model memory allocator. - */ -static inline void checkpoint_take(struct lp_ctx *lp) -{ - timer_uint t = timer_hr_new(); - model_allocator_checkpoint_take(array_count(lp->p.p_msgs)); - stats_take(STATS_CKPT_STATE_SIZE, lp->mm_state.used_mem); - stats_take(STATS_CKPT, 1); - stats_take(STATS_CKPT_TIME, timer_hr_value(t)); -} - /** * @brief Initializes the processing module in the current LP */ @@ -100,8 +85,8 @@ void process_lp_init(void) common_msg_process(lp, msg); array_push(lp->p.p_msgs, msg); - model_allocator_checkpoint_next_force_full(); - checkpoint_take(lp); + model_allocator_checkpoint_next_force_full(&lp->mm_state); + model_allocator_checkpoint_take(&lp->mm_state, array_count(lp->p.p_msgs)); } /** @@ -141,7 +126,7 @@ void process_lp_fini(void) * * This function implements the coasting forward operation done after a checkpoint has been restored. */ -static inline void silent_execution(const struct process_data *proc_p, array_count_t last_i, array_count_t past_i) +static inline void silent_execution(const struct lp_ctx *lp, array_count_t last_i, array_count_t past_i) { if(unlikely(last_i >= past_i)) return; @@ -149,11 +134,11 @@ static inline void silent_execution(const struct process_data *proc_p, array_cou timer_uint t = timer_hr_new(); silent_processing = true; - void *state_p = current_lp->state_pointer; + void *state_p = lp->state_pointer; do { - const struct lp_msg *msg = array_get_at(proc_p->p_msgs, last_i); + const struct lp_msg *msg = array_get_at(lp->p.p_msgs, last_i); while(is_msg_sent(msg)) - msg = array_get_at(proc_p->p_msgs, ++last_i); + msg = array_get_at(lp->p.p_msgs, ++last_i); global_config.dispatcher(msg->dest, msg->dest_t, msg->m_type, msg->pl, msg->pl_size, state_p); stats_take(STATS_MSG_SILENT, 1); @@ -206,14 +191,14 @@ static inline void send_anti_messages(struct process_data *proc_p, array_count_t * @param proc_p the message processing data for the LP that has to rollback * @param past_i the index in @a proc_p of the last validly processed message */ -static void do_rollback(struct process_data *proc_p, array_count_t past_i) +static void do_rollback(struct lp_ctx *lp, array_count_t past_i) { timer_uint t = timer_hr_new(); - send_anti_messages(proc_p, past_i); - array_count_t last_i = model_allocator_checkpoint_restore(past_i); + send_anti_messages(&lp->p, past_i); + array_count_t last_i = model_allocator_checkpoint_restore(&lp->mm_state, past_i); stats_take(STATS_RECOVERY_TIME, timer_hr_value(t)); stats_take(STATS_ROLLBACK, 1); - silent_execution(proc_p, last_i, past_i); + silent_execution(lp, last_i, past_i); } /** @@ -265,26 +250,26 @@ static inline array_count_t match_anti_msg(const struct process_data *proc_p, co * @param proc_p the message processing data for the LP that has to handle the anti-message * @param a_msg the remote anti-message */ -static inline void handle_remote_anti_msg(struct process_data *proc_p, struct lp_msg *a_msg) +static inline void handle_remote_anti_msg(struct lp_ctx *lp, struct lp_msg *a_msg) { // Simplifies flags-based matching, also useful in the early remote anti-messages matching a_msg->raw_flags -= MSG_FLAG_ANTI; uint32_t m_id = a_msg->raw_flags, m_seq = a_msg->m_seq; - array_count_t i = array_count(proc_p->p_msgs); + array_count_t i = array_count(lp->p.p_msgs); struct lp_msg *msg; do { if(unlikely(!i)) { // Sadly this is an early remote anti-message - a_msg->next = proc_p->early_antis; - proc_p->early_antis = a_msg; + a_msg->next = lp->p.early_antis; + lp->p.early_antis = a_msg; return; } - msg = array_get_at(proc_p->p_msgs, --i); + msg = array_get_at(lp->p.p_msgs, --i); } while(is_msg_sent(msg) || msg->raw_flags != m_id || msg->m_seq != m_seq); while(i) { - const struct lp_msg *v_msg = array_get_at(proc_p->p_msgs, --i); + const struct lp_msg *v_msg = array_get_at(lp->p.p_msgs, --i); if(is_msg_past(v_msg)) { i++; break; @@ -292,7 +277,7 @@ static inline void handle_remote_anti_msg(struct process_data *proc_p, struct lp } msg->raw_flags |= MSG_FLAG_ANTI; - do_rollback(proc_p, i); + do_rollback(lp, i); termination_on_lp_rollback(msg->dest_t); msg_allocator_free(msg); msg_allocator_free(a_msg); @@ -331,12 +316,12 @@ static inline bool check_early_anti_messages(struct process_data *proc_p, struct static void handle_anti_msg(struct lp_ctx *lp, struct lp_msg *msg, uint32_t last_flags) { if(last_flags > (MSG_FLAG_ANTI | MSG_FLAG_PROCESSED)) { - handle_remote_anti_msg(&lp->p, msg); + handle_remote_anti_msg(lp, msg); auto_ckpt_register_bad(&lp->auto_ckpt); return; } else if(last_flags == (MSG_FLAG_ANTI | MSG_FLAG_PROCESSED)) { array_count_t past_i = match_anti_msg(&lp->p, msg); - do_rollback(&lp->p, past_i); + do_rollback(lp, past_i); termination_on_lp_rollback(msg->dest_t); auto_ckpt_register_bad(&lp->auto_ckpt); } @@ -351,7 +336,7 @@ static void handle_anti_msg(struct lp_ctx *lp, struct lp_msg *msg, uint32_t last static void handle_straggler_msg(struct lp_ctx *lp, struct lp_msg *msg) { array_count_t past_i = match_straggler_msg(&lp->p, msg); - do_rollback(&lp->p, past_i); + do_rollback(lp, past_i); termination_on_lp_rollback(msg->dest_t); auto_ckpt_register_bad(&lp->auto_ckpt); } @@ -400,7 +385,7 @@ void process_msg(void) auto_ckpt_register_good(&lp->auto_ckpt); if(auto_ckpt_is_needed(&lp->auto_ckpt)) - checkpoint_take(lp); + model_allocator_checkpoint_take(&lp->mm_state, array_count(lp->p.p_msgs)); termination_on_msg_process(msg->dest_t); } diff --git a/src/mm/buddy/buddy.c b/src/mm/buddy/buddy.c index 529f1485c..4775f0a21 100644 --- a/src/mm/buddy/buddy.c +++ b/src/mm/buddy/buddy.c @@ -1,7 +1,7 @@ /** * @file mm/buddy/buddy.c * - * @brief A Buddy System implementation + * @brief A rollbackable Buddy System implementation * * SPDX-FileCopyrightText: 2008-2022 HPDCS Group * SPDX-License-Identifier: GPL-3.0-only diff --git a/src/mm/buddy/buddy.h b/src/mm/buddy/buddy.h index 9389a5074..336908efb 100644 --- a/src/mm/buddy/buddy.h +++ b/src/mm/buddy/buddy.h @@ -1,7 +1,7 @@ /** * @file mm/buddy/buddy.h * - * @brief A Buddy System implementation + * @brief A rollbackable Buddy System implementation * * SPDX-FileCopyrightText: 2008-2022 HPDCS Group * SPDX-License-Identifier: GPL-3.0-only diff --git a/src/mm/buddy/ckpt.c b/src/mm/buddy/ckpt.c index 93387db42..2a74fccae 100644 --- a/src/mm/buddy/ckpt.c +++ b/src/mm/buddy/ckpt.c @@ -1,7 +1,7 @@ /** * @file mm/buddy/ckpt.c * - * @brief Checkpointing capabilities + * @brief Checkpointing capabilities for the rollbackable buddy system * * SPDX-FileCopyrightText: 2008-2022 HPDCS Group * SPDX-License-Identifier: GPL-3.0-only diff --git a/src/mm/buddy/ckpt.h b/src/mm/buddy/ckpt.h index ff79c648f..811e6d32b 100644 --- a/src/mm/buddy/ckpt.h +++ b/src/mm/buddy/ckpt.h @@ -1,7 +1,7 @@ /** * @file mm/buddy/ckpt.h * - * @brief Checkpointing capabilities + * @brief Checkpointing capabilities for the rollbackable buddy system * * SPDX-FileCopyrightText: 2008-2022 HPDCS Group * SPDX-License-Identifier: GPL-3.0-only diff --git a/src/mm/buddy/multi.c b/src/mm/buddy/multi_buddy.c similarity index 77% rename from src/mm/buddy/multi.c rename to src/mm/buddy/multi_buddy.c index db9262bb8..3bc6817b4 100644 --- a/src/mm/buddy/multi.c +++ b/src/mm/buddy/multi_buddy.c @@ -1,12 +1,12 @@ /** * @file mm/buddy/multi.c * - * @brief Handling of multiple buddy systems + * @brief Handling of multiple rollbackable buddy systems * * SPDX-FileCopyrightText: 2008-2022 HPDCS Group * SPDX-License-Identifier: GPL-3.0-only */ -#include +#include #include #include @@ -22,18 +22,15 @@ #define is_log_incremental(l) false #endif -void model_allocator_lp_init(void) +void multi_buddy_lp_init(struct multi_buddy_state *self) { - struct mm_state *self = ¤t_lp->mm_state; array_init(self->buddies); array_init(self->logs); self->used_mem = 0; } -void model_allocator_lp_fini(void) +void multi_buddy_lp_fini(struct multi_buddy_state *self) { - struct mm_state *self = ¤t_lp->mm_state; - array_count_t i = array_count(self->logs); while(i--) mm_free(array_get_at(self->logs, i).c); @@ -47,11 +44,8 @@ void model_allocator_lp_fini(void) array_fini(self->buddies); } -void *rs_malloc(size_t req_size) +void *multi_buddy_alloc(struct multi_buddy_state *self, size_t req_size) { - if(unlikely(!req_size)) - return NULL; - uint_fast8_t req_blks_exp = buddy_allocation_block_compute(req_size); if(unlikely(req_blks_exp > B_TOTAL_EXP)) { errno = ENOMEM; @@ -59,7 +53,6 @@ void *rs_malloc(size_t req_size) return NULL; } - struct mm_state *self = ¤t_lp->mm_state; self->used_mem += 1 << req_blks_exp; array_count_t i = array_count(self->buddies); @@ -80,18 +73,7 @@ void *rs_malloc(size_t req_size) return buddy_malloc(new_buddy, req_blks_exp); } -void *rs_calloc(size_t nmemb, size_t size) -{ - size_t tot = nmemb * size; - void *ret = rs_malloc(tot); - - if(likely(ret)) - memset(ret, 0, tot); - - return ret; -} - -static inline struct buddy_state *buddy_find_by_address(struct mm_state *self, const void *ptr) +static inline struct buddy_state *buddy_find_by_address(struct multi_buddy_state *self, const void *ptr) { array_count_t l = 0, h = array_count(self->buddies) - 1; while(1) { @@ -106,27 +88,14 @@ static inline struct buddy_state *buddy_find_by_address(struct mm_state *self, c } } -void rs_free(void *ptr) +void multi_buddy_free(struct multi_buddy_state *self, void *ptr) { - if(unlikely(!ptr)) - return; - - struct mm_state *self = ¤t_lp->mm_state; struct buddy_state *b = buddy_find_by_address(self, ptr); self->used_mem -= buddy_free(b, ptr); } -void *rs_realloc(void *ptr, size_t req_size) +void *multi_buddy_realloc(struct multi_buddy_state *self, void *ptr, size_t req_size) { - if(!req_size) { // Adhering to C11 standard §7.20.3.1 - if(!ptr) - errno = EINVAL; - return NULL; - } - if(!ptr) - return rs_malloc(req_size); - - struct mm_state *self = ¤t_lp->mm_state; struct buddy_state *b = buddy_find_by_address(self, ptr); struct buddy_realloc_res ret = buddy_best_effort_realloc(b, ptr, req_size); if(ret.handled) { @@ -144,9 +113,8 @@ void *rs_realloc(void *ptr, size_t req_size) return new_buffer; } -void __write_mem(const void *ptr, size_t s) +void multi_buddy_dirty_mark(struct multi_buddy_state *self, const void *ptr, size_t s) { - struct mm_state *self = ¤t_lp->mm_state; if(unlikely(!s || array_is_empty(self->buddies))) return; @@ -159,9 +127,8 @@ void __write_mem(const void *ptr, size_t s) } // todo: incremental -void model_allocator_checkpoint_take(array_count_t ref_i) +void multi_buddy_checkpoint_take(struct multi_buddy_state *self, array_count_t ref_i) { - struct mm_state *self = ¤t_lp->mm_state; size_t buddies_size = offsetof(struct buddy_checkpoint, base_mem) * array_count(self->buddies); struct mm_checkpoint *ckp = mm_alloc( offsetof(struct mm_checkpoint, chkps) + buddies_size + self->used_mem + sizeof(struct buddy_state *)); @@ -177,14 +144,14 @@ void model_allocator_checkpoint_take(array_count_t ref_i) buddy_ckp->orig = NULL; } -void model_allocator_checkpoint_next_force_full(void) +void multi_buddy_checkpoint_next_force_full(struct multi_buddy_state *self) { // TODO: force full checkpointing when incremental state saving is enabled + (void) self; } -array_count_t model_allocator_checkpoint_restore(array_count_t ref_i) +array_count_t multi_buddy_checkpoint_restore(struct multi_buddy_state *self, array_count_t ref_i) { - struct mm_state *self = ¤t_lp->mm_state; array_count_t i = array_count(self->logs) - 1; while(array_get_at(self->logs, i).ref_i > ref_i) i--; @@ -210,7 +177,7 @@ array_count_t model_allocator_checkpoint_restore(array_count_t ref_i) return array_get_at(self->logs, i).ref_i; } -array_count_t model_allocator_fossil_lp_collect(struct mm_state *self, array_count_t tgt_ref_i) +array_count_t multi_buddy_fossil_lp_collect(struct multi_buddy_state *self, array_count_t tgt_ref_i) { array_count_t log_i = array_count(self->logs) - 1; array_count_t ref_i = array_get_at(self->logs, log_i).ref_i; diff --git a/src/mm/buddy/multi.h b/src/mm/buddy/multi_buddy.h similarity index 57% rename from src/mm/buddy/multi.h rename to src/mm/buddy/multi_buddy.h index abd2745ad..e243f85e7 100644 --- a/src/mm/buddy/multi.h +++ b/src/mm/buddy/multi_buddy.h @@ -1,7 +1,7 @@ /** * @file mm/buddy/multi.h * - * @brief Handling of multiple buddy systems + * @brief Handling of multiple rollbackable buddy systems * * SPDX-FileCopyrightText: 2008-2022 HPDCS Group * SPDX-License-Identifier: GPL-3.0-only @@ -32,12 +32,21 @@ struct mm_log { }; /// The checkpointable memory context assigned to a single LP -struct mm_state { +struct multi_buddy_state { + /// The total count of allocated bytes + uint_fast32_t used_mem; /// The array of pointers to the allocated buddy systems for the LP dyn_array(struct buddy_state *) buddies; /// The array of checkpoints dyn_array(struct mm_log) logs; - /// The total count of allocated bytes - uint_fast32_t used_mem; }; +extern void multi_buddy_lp_init(struct multi_buddy_state *m); +extern void multi_buddy_lp_fini(struct multi_buddy_state *m); +extern void *multi_buddy_alloc(struct multi_buddy_state *m, size_t s); +extern void multi_buddy_free(struct multi_buddy_state *m, void *ptr); +extern void *multi_buddy_realloc(struct multi_buddy_state *m, void *ptr, size_t s); +extern void multi_buddy_checkpoint_take(struct multi_buddy_state *m, array_count_t ref_i); +extern void multi_buddy_checkpoint_next_force_full(struct multi_buddy_state *m); +extern array_count_t multi_buddy_checkpoint_restore(struct multi_buddy_state *m, array_count_t ref_i); +extern array_count_t multi_buddy_fossil_lp_collect(struct multi_buddy_state *m, array_count_t tgt_ref_i); diff --git a/src/mm/dymelor/checkpoint.c b/src/mm/dymelor/checkpoint.c new file mode 100644 index 000000000..176862f52 --- /dev/null +++ b/src/mm/dymelor/checkpoint.c @@ -0,0 +1,142 @@ +/** + * @file mm/dymelor/checkpoint.h + * + * @brief Checkpointing capabilities for DyMeLoR + * + * SPDX-FileCopyrightText: 2008-2022 HPDCS Group + * SPDX-License-Identifier: GPL-3.0-only + */ +#include + +#include +#include +#include + +static size_t compute_log_size(const struct dymelor_state *ctx) +{ + size_t ret = sizeof(unsigned) + offsetof(struct dymelor_state_checkpoint, data); + for(unsigned i = 0; i < NUM_AREAS; ++i) { + const struct dymelor_area *area = ctx->areas[i]; + uint_least32_t num_chunks = MIN_NUM_CHUNKS; + while(area != NULL) { + ret += sizeof(offsetof(struct dymelor_area_checkpoint, data)); + ret += bitmap_required_size(num_chunks); +#ifdef ROOTSIM_INCREMENTAL + ret += bitmap_required_size(num_chunks); // maybe not needed in full checkpoints +#endif + ret += area->alloc_chunks << area->chk_size_exp; + ret -= area->alloc_chunks * sizeof(uint_least32_t); + + num_chunks *= 2; + area = area->next; + } + } + return ret; +} + +struct dymelor_state_checkpoint *dymelor_checkpoint_full_take(const struct dymelor_state *ctx) +{ + struct dymelor_state_checkpoint *ret = mm_alloc(compute_log_size(ctx)); + ret->used_mem = ctx->used_mem; + struct dymelor_area_checkpoint *ckpt = (struct dymelor_area_checkpoint *)ret->data; + for(unsigned i = 0; i < NUM_AREAS; ++i) { + const struct dymelor_area *area = ctx->areas[i]; + uint_least32_t num_chunks = MIN_NUM_CHUNKS; + uint_least32_t chunk_size = (((uint_least32_t)1U) << (MIN_CHUNK_EXP + i)) - sizeof(uint_least32_t); + while(area != NULL) { + ckpt->i = i; + ckpt->chunk_cnt = area->alloc_chunks; + size_t bitmap_size = bitmap_required_size(num_chunks); + memcpy(ckpt->data, area->use_bitmap, bitmap_size); + unsigned char *ptr = ckpt->data + bitmap_size; + +#define copy_from_area(x) \ + ({ \ + memcpy(ptr, area->area + ((x) * (chunk_size + sizeof(uint_least32_t))), chunk_size); \ + ptr += chunk_size; \ + }) + + // Copy only the allocated chunks + bitmap_foreach_set(area->use_bitmap, bitmap_size, copy_from_area); + +#undef copy_from_area + + num_chunks *= 2; + area = area->next; + ckpt = (struct dymelor_area_checkpoint *)ptr; + } + } + ckpt->i = UINT_MAX; + + return ret; +} + +void dymelor_checkpoint_full_restore(struct dymelor_state *ctx, const struct dymelor_state_checkpoint *state_ckpt) +{ + ctx->used_mem = state_ckpt->used_mem; + const struct dymelor_area_checkpoint *ckpt = (const struct dymelor_area_checkpoint *)state_ckpt->data; + for(unsigned i = 0; i < sizeof(ctx->areas) / sizeof(*ctx->areas); ++i) { + struct dymelor_area *area = ctx->areas[i]; + uint_least32_t num_chunks = MIN_NUM_CHUNKS; + if(i == ckpt->i) { + uint_least32_t chunk_size = (1U << (MIN_CHUNK_EXP + i)) - sizeof(uint_least32_t); + do { + area->alloc_chunks = ckpt->chunk_cnt; + size_t bitmap_size = bitmap_required_size(num_chunks); + memcpy(area->use_bitmap, ckpt->data, bitmap_size); + const unsigned char *ptr = ckpt->data + bitmap_size; + +#define copy_to_area(x) \ + ({ \ + memcpy(area->area + ((x) * (chunk_size + sizeof(uint_least32_t))), ptr, chunk_size); \ + ptr += chunk_size; \ + }) + + bitmap_foreach_set(area->use_bitmap, bitmap_size, copy_to_area); +#undef copy_to_area + num_chunks *= 2; + area = area->next; + ckpt = (const struct dymelor_area_checkpoint *)ptr; + } while(i == ckpt->i); + } + + while(area != NULL) { + area->alloc_chunks = 0; + memset(area->use_bitmap, 0, bitmap_required_size(num_chunks)); + num_chunks *= 2; + area = area->next; + } + } +} + +void dymelor_checkpoint_trim_to(struct dymelor_state *ctx, const struct dymelor_state_checkpoint *state_ckpt) +{ + const struct dymelor_area_checkpoint *ckpt = (const struct dymelor_area_checkpoint *)state_ckpt->data; + for(unsigned i = 0; i < sizeof(ctx->areas) / sizeof(*ctx->areas); ++i) { + struct dymelor_area **next_area_p = &ctx->areas[i]; + if(i == ckpt->i) { + uint_least32_t chunk_size = (1U << (MIN_CHUNK_EXP + i)) - sizeof(uint_least32_t); + uint_least32_t num_chunks = MIN_NUM_CHUNKS; + do { + struct dymelor_area *area = *next_area_p; + area->alloc_chunks = ckpt->chunk_cnt; + size_t bitmap_size = bitmap_required_size(num_chunks); + memcpy(area->use_bitmap, ckpt->data, bitmap_size); + const unsigned char *ptr = ckpt->data + bitmap_size; + uint_least32_t set = bitmap_count_set(area->use_bitmap, bitmap_size); + ptr += set * chunk_size; + num_chunks *= 2; + next_area_p = &area->next; + ckpt = (const struct dymelor_area_checkpoint *)ptr; + } while(i == ckpt->i); + } + + struct dymelor_area *area = *next_area_p; + *next_area_p = NULL; + while(area != NULL) { + struct dymelor_area *next_area = area->next; + mm_free(area); + area = next_area; + } + } +} diff --git a/src/mm/dymelor/checkpoint.h b/src/mm/dymelor/checkpoint.h new file mode 100644 index 000000000..f7a082c3b --- /dev/null +++ b/src/mm/dymelor/checkpoint.h @@ -0,0 +1,31 @@ +/** + * @file mm/dymelor/checkpoint.h + * + * @brief Checkpointing capabilities for DyMeLoR + * + * SPDX-FileCopyrightText: 2008-2022 HPDCS Group + * SPDX-License-Identifier: GPL-3.0-only + */ +#pragma once + +#include + +#include + +struct dymelor_state_checkpoint { + uint_fast32_t used_mem; + unsigned char data[]; +}; + +struct dymelor_area_checkpoint { + unsigned i; + uint_least32_t chunk_cnt; + unsigned char data[]; +}; + +_Static_assert(alignof(struct dymelor_area_checkpoint) <= sizeof(uint_least32_t), + "Adjacent checkpoints may not satisfy memory access alignment requirements"); + +extern struct dymelor_state_checkpoint *dymelor_checkpoint_full_take(const struct dymelor_state *ctx); +extern void dymelor_checkpoint_full_restore(struct dymelor_state *ctx, const struct dymelor_state_checkpoint *ckpt); +extern void dymelor_checkpoint_trim_to(struct dymelor_state *ctx, const struct dymelor_state_checkpoint *state_ckpt); diff --git a/src/mm/dymelor/dymelor.c b/src/mm/dymelor/dymelor.c new file mode 100644 index 000000000..63989e0da --- /dev/null +++ b/src/mm/dymelor/dymelor.c @@ -0,0 +1,209 @@ +/** + * @file mm/dymelor/dymelor.h + * + * @brief Dynamic Memory Logger and Restorer Library + * + * Implementation of the DyMeLoR memory allocator + * + * SPDX-FileCopyrightText: 2008-2022 HPDCS Group + * SPDX-License-Identifier: GPL-3.0-only + */ +#include + +#include +#include +#include + +#include + +#define next_exp_of_2(i) (sizeof(i) * CHAR_BIT - intrinsics_clz(i)) + +void dymelor_lp_init(struct dymelor_state *self) +{ + memset(self->areas, 0, sizeof(self->areas)); + self->used_mem = 0; + array_init(self->logs); +} + +void dymelor_lp_fini(struct dymelor_state *self) +{ + for(array_count_t i = 0; i < array_count(self->logs); ++i) + mm_free(array_get_at(self->logs, i).c); + + array_fini(self->logs); + + for(unsigned i = 0; i < NUM_AREAS; ++i) { + struct dymelor_area *area = self->areas[i]; + while(area != NULL) { + struct dymelor_area *next = area->next; + mm_free(area); + area = next; + } + } +} + +void dymelor_checkpoint_take(struct dymelor_state *self, array_count_t ref_i) +{ + struct dymelor_log mm_log = {.ref_i = ref_i, .c = dymelor_checkpoint_full_take(self)}; + array_push(self->logs, mm_log); +} + +void dymelor_checkpoint_next_force_full(struct dymelor_state *self) +{ + (void)self; + // todo: support incremental checkpointing properly +} + +array_count_t dymelor_checkpoint_restore(struct dymelor_state *self, array_count_t ref_i) +{ + array_count_t i = array_count(self->logs) - 1; + while(array_get_at(self->logs, i).ref_i > ref_i) + i--; + + dymelor_checkpoint_full_restore(self, array_get_at(self->logs, i).c); + + for(array_count_t j = array_count(self->logs) - 1; j > i; --j) + mm_free(array_get_at(self->logs, j).c); + + array_count(self->logs) = i + 1; + return array_get_at(self->logs, i).ref_i; +} + +array_count_t dymelor_fossil_lp_collect(struct dymelor_state *self, array_count_t tgt_ref_i) +{ + array_count_t log_i = array_count(self->logs) - 1; + array_count_t ref_i = array_get_at(self->logs, log_i).ref_i; + while(ref_i > tgt_ref_i) { + --log_i; + ref_i = array_get_at(self->logs, log_i).ref_i; + } + + array_count_t j = array_count(self->logs); + while(j > log_i) { + --j; + array_get_at(self->logs, j).ref_i -= ref_i; + } + + while(j--) + mm_free(array_get_at(self->logs, j).c); + + array_truncate_first(self->logs, log_i); + return ref_i; +} + +static struct dymelor_area *malloc_area_new(unsigned chunk_size_exp, uint_least32_t num_chunks) +{ + size_t bitmap_size = bitmap_required_size(num_chunks); + struct dymelor_area *area = + mm_alloc(offsetof(struct dymelor_area, area) + 2 * bitmap_size + (num_chunks << chunk_size_exp)); + area->alloc_chunks = 0; + area->dirty_chunks = 0; + area->last_chunk = 0; + area->chk_size_exp = chunk_size_exp; + area->use_bitmap = area->area + (num_chunks << chunk_size_exp); + area->dirty_bitmap = area->use_bitmap + bitmap_size; + memset(area->use_bitmap, 0, 2 * bitmap_size); + area->next = NULL; + return area; +} + +void *dymelor_alloc(struct dymelor_state *self, size_t req_size) +{ + if(unlikely(req_size > MAX_CHUNK_SIZE)) { + logger(LOG_ERROR, + "Requested a memory allocation of %d but the limit is %d. Reconfigure MAX_CHUNK_SIZE. Returning NULL", + req_size, MAX_CHUNK_SIZE); + return NULL; + } + + req_size += sizeof(uint_least32_t); + unsigned size_exp = max(next_exp_of_2(req_size), MIN_CHUNK_EXP); + + uint_least32_t num_chunks = MIN_NUM_CHUNKS; + struct dymelor_area **m_area_p = &self->areas[size_exp - MIN_CHUNK_EXP]; + struct dymelor_area *m_area = *m_area_p; + while(m_area != NULL && m_area->alloc_chunks >= num_chunks) { + m_area_p = &m_area->next; + m_area = *m_area_p; + num_chunks *= 2; + } + + if(unlikely(m_area == NULL)) { + *m_area_p = malloc_area_new(size_exp, num_chunks); + m_area = *m_area_p; + } + + while(bitmap_check(m_area->use_bitmap, m_area->last_chunk)) + m_area->last_chunk++; + + bitmap_set(m_area->use_bitmap, m_area->last_chunk); + ++m_area->alloc_chunks; + self->used_mem += 1U << size_exp; + uint_least32_t offset = m_area->last_chunk << size_exp; + m_area->last_chunk++; + + unsigned char *ptr = m_area->area + offset; + *(uint_least32_t *)(ptr - sizeof(uint_least32_t)) = offset + offsetof(struct dymelor_area, area); + return ptr; +} + +void *dymelor_realloc(struct dymelor_state *self, void *ptr, size_t req_size) +{ + if(!req_size) { // Adhering to C11 standard §7.20.3.1 + if(!ptr) + errno = EINVAL; + return NULL; + } + if(!ptr) + return rs_malloc(req_size); + + unsigned char *p = ptr; + struct dymelor_area *m_area = (struct dymelor_area *)(p - *(uint_least32_t *)(p - sizeof(uint_least32_t))); + void *new_buffer = dymelor_alloc(self, req_size); + if(unlikely(new_buffer == NULL)) + return NULL; + + memcpy(new_buffer, ptr, min(req_size, (1U << m_area->chk_size_exp) - sizeof(uint_least32_t))); + rs_free(ptr); + + return new_buffer; +} + +void dymelor_free(struct dymelor_state *self, void *ptr) +{ + unsigned char *p = ptr; + struct dymelor_area *m_area = (struct dymelor_area *)(p - *(uint_least32_t *)(p - sizeof(uint_least32_t))); + + uint_least32_t idx = (p - m_area->area) >> m_area->chk_size_exp; + if(!bitmap_check(m_area->use_bitmap, idx)) { + logger(LOG_FATAL, "double free() corruption or address not malloc'd\n"); + abort(); + } + bitmap_reset(m_area->use_bitmap, idx); + --m_area->alloc_chunks; + self->used_mem -= 1U << m_area->chk_size_exp; + +#ifdef ROOTSIM_INCREMENTAL + if(bitmap_check(m_area->dirty_bitmap, idx)) { + bitmap_reset(m_area->dirty_bitmap, idx); + m_area->dirty_chunks--; + } +#endif + + if(idx < m_area->last_chunk) + m_area->last_chunk = idx; +} + +void dymelor_dirty_mark(struct dymelor_state *self, void *base, int size) +{ + (void)self, (void)size; + // FIXME: check base belongs to the LP memory allocator; doing this efficiently requires DyMeLoR memory areas to + // come from the same underlying allocator (maybe use a large grained buddy system underneath) + unsigned char *p = base; + struct dymelor_area *m_area = (struct dymelor_area *)(p - *(uint_least32_t *)(p - sizeof(uint_least32_t))); + uint_least32_t i = (p - m_area->area) >> m_area->chk_size_exp; + if(!bitmap_check(m_area->dirty_bitmap, i)) { + bitmap_set(m_area->dirty_bitmap, i); + m_area->dirty_chunks++; + } +} diff --git a/src/mm/dymelor/dymelor.h b/src/mm/dymelor/dymelor.h new file mode 100644 index 000000000..7c328bb11 --- /dev/null +++ b/src/mm/dymelor/dymelor.h @@ -0,0 +1,67 @@ +/** + * @file mm/dymelor/dymelor.h + * + * @brief Dynamic Memory Logger and Restorer Library + * + * Implementation of the DyMeLoR memory allocator + * + * SPDX-FileCopyrightText: 2008-2022 HPDCS Group + * SPDX-License-Identifier: GPL-3.0-only + */ +#pragma once + +#include +#include +#include + +#include +#include + +#define MIN_CHUNK_EXP 6U // Size (in bytes) of the smallest chunk provideable by DyMeLoR +#define MAX_CHUNK_EXP 21U // Size (in bytes) of the biggest one. Notice that if this number is too large, performance + // (and memory usage) might be affected. If it is too small, large amount of memory requests + // by the application level software (i.e, larger than this number) will fail, as DyMeLoR + // will not be able to handle them! + +#define MAX_CHUNK_SIZE ((1U << MAX_CHUNK_EXP) - sizeof(uint_least32_t)) +#define NUM_AREAS (MAX_CHUNK_EXP - MIN_CHUNK_EXP + 1) +#define MIN_NUM_CHUNKS 512 // Minimum number of chunks per malloc_area + + +/// This structure let DyMeLoR handle one malloc area (for serving given-size memory requests) +struct dymelor_area { + uint_least32_t alloc_chunks; + uint_least32_t dirty_chunks; + uint_least32_t last_chunk; + unsigned chk_size_exp; + block_bitmap *use_bitmap; + block_bitmap *dirty_bitmap; + struct dymelor_area *next; + uint_least32_t first_back_p; + alignas(16) unsigned char area[]; +}; + +struct dymelor_log { + /// The reference index, used to identify this checkpoint + array_count_t ref_i; + /// A pointer to the actual checkpoint + struct dymelor_state_checkpoint *c; +}; + +/// Definition of the memory map +struct dymelor_state { + uint_fast32_t used_mem; + struct dymelor_area *areas[NUM_AREAS]; + /// The array of checkpoints + dyn_array(struct dymelor_log) logs; +}; + +extern void dymelor_lp_init(struct dymelor_state *m); +extern void dymelor_lp_fini(struct dymelor_state *m); +extern void *dymelor_alloc(struct dymelor_state *m, size_t s); +extern void dymelor_free(struct dymelor_state *m, void *ptr); +extern void *dymelor_realloc(struct dymelor_state *m, void *ptr, size_t s); +extern void dymelor_checkpoint_take(struct dymelor_state *m, array_count_t ref_i); +extern void dymelor_checkpoint_next_force_full(struct dymelor_state *m); +extern array_count_t dymelor_checkpoint_restore(struct dymelor_state *m, array_count_t ref_i); +extern array_count_t dymelor_fossil_lp_collect(struct dymelor_state *m, array_count_t tgt_ref_i); diff --git a/src/mm/model_allocator.c b/src/mm/model_allocator.c new file mode 100644 index 000000000..70dba6f93 --- /dev/null +++ b/src/mm/model_allocator.c @@ -0,0 +1,177 @@ +/** + * @file mm/model_allocator.c + * + * @brief Memory management functions for simulation models + * + * Memory management functions for simulation models + * + * SPDX-FileCopyrightText: 2008-2022 HPDCS Group + * SPDX-License-Identifier: GPL-3.0-only + */ +#include + +#include +#include +#include + +#include + +void model_allocator_lp_init(struct mm_state *self) +{ + switch(global_config.mm) { + case MM_MULTI_BUDDY: + multi_buddy_lp_init(&self->m_mb); + break; + case MM_DYMELOR: + dymelor_lp_init(&self->m_dy); + break; + default: + assert(0); + __builtin_unreachable(); + } +} + +void model_allocator_lp_fini(struct mm_state *self) +{ + switch(global_config.mm) { + case MM_MULTI_BUDDY: + multi_buddy_lp_fini(&self->m_mb); + break; + case MM_DYMELOR: + dymelor_lp_fini(&self->m_dy); + break; + default: + assert(0); + __builtin_unreachable(); + } +} + +void model_allocator_checkpoint_take(struct mm_state *self, array_count_t ref_i) +{ + timer_uint t = timer_hr_new(); + switch(global_config.mm) { + case MM_MULTI_BUDDY: + multi_buddy_checkpoint_take(&self->m_mb, ref_i); + break; + case MM_DYMELOR: + dymelor_checkpoint_take(&self->m_dy, ref_i); + break; + default: + assert(0); + __builtin_unreachable(); + } + stats_take(STATS_CKPT, 1); + stats_take(STATS_CKPT_STATE_SIZE, self->used_mem); + stats_take(STATS_CKPT_TIME, timer_hr_value(t)); +} + +void model_allocator_checkpoint_next_force_full(struct mm_state *self) +{ + switch(global_config.mm) { + case MM_MULTI_BUDDY: + multi_buddy_checkpoint_next_force_full(&self->m_mb); + break; + case MM_DYMELOR: + dymelor_checkpoint_next_force_full(&self->m_dy); + break; + default: + assert(0); + __builtin_unreachable(); + } +} + +array_count_t model_allocator_checkpoint_restore(struct mm_state *self, array_count_t ref_i) +{ + switch(global_config.mm) { + case MM_MULTI_BUDDY: + return multi_buddy_checkpoint_restore(&self->m_mb, ref_i); + case MM_DYMELOR: + return dymelor_checkpoint_restore(&self->m_dy, ref_i); + default: + assert(0); + __builtin_unreachable(); + } +} + +array_count_t model_allocator_fossil_lp_collect(struct mm_state *self, array_count_t tgt_ref_i) +{ + switch(global_config.mm) { + case MM_MULTI_BUDDY: + return multi_buddy_fossil_lp_collect(&self->m_mb, tgt_ref_i); + case MM_DYMELOR: + return dymelor_fossil_lp_collect(&self->m_dy, tgt_ref_i); + default: + assert(0); + __builtin_unreachable(); + } +} + +void *rs_malloc(size_t req_size) +{ + if(unlikely(!req_size)) + return NULL; + + struct mm_state *self = ¤t_lp->mm_state; + switch(global_config.mm) { + case MM_MULTI_BUDDY: + return multi_buddy_alloc(&self->m_mb, req_size); + case MM_DYMELOR: + return dymelor_alloc(&self->m_dy, req_size); + default: + assert(0); + __builtin_unreachable(); + } +} + +void *rs_calloc(size_t nmemb, size_t size) +{ + size_t tot = nmemb * size; + void *ret = rs_malloc(tot); + + if(likely(ret)) + memset(ret, 0, tot); + + return ret; +} + +void *rs_realloc(void *ptr, size_t req_size) +{ + if(!req_size) { // Adhering to C11 standard §7.20.3.1 + if(unlikely(!ptr)) + errno = EINVAL; + return NULL; + } + + if(!ptr) + return rs_malloc(req_size); + + struct mm_state *self = ¤t_lp->mm_state; + switch(global_config.mm) { + case MM_MULTI_BUDDY: + return multi_buddy_realloc(&self->m_mb, ptr, req_size); + case MM_DYMELOR: + return dymelor_realloc(&self->m_dy, ptr, req_size); + default: + assert(0); + __builtin_unreachable(); + } +} + +void rs_free(void *ptr) +{ + if(unlikely(ptr == NULL)) + return; + + struct mm_state *self = ¤t_lp->mm_state; + switch(global_config.mm) { + case MM_MULTI_BUDDY: + multi_buddy_free(&self->m_mb, ptr); + break; + case MM_DYMELOR: + dymelor_free(&self->m_dy, ptr); + break; + default: + assert(0); + __builtin_unreachable(); + } +} diff --git a/src/mm/model_allocator.h b/src/mm/model_allocator.h index 5fc869c3e..f4bb4e317 100644 --- a/src/mm/model_allocator.h +++ b/src/mm/model_allocator.h @@ -10,12 +10,28 @@ */ #pragma once -#include -#include +#include -extern void model_allocator_lp_init(void); -extern void model_allocator_lp_fini(void); -extern void model_allocator_checkpoint_take(array_count_t ref_i); -extern void model_allocator_checkpoint_next_force_full(void); -extern array_count_t model_allocator_checkpoint_restore(array_count_t ref_i); +#include +#include + +struct mm_state { + union { + uint_fast32_t used_mem; + struct multi_buddy_state m_mb; + struct dymelor_state m_dy; + }; +}; +_Static_assert(offsetof(struct mm_state, used_mem) == offsetof(struct multi_buddy_state, used_mem), + "Can't access used memory field in an homogeneous way!"); +_Static_assert(offsetof(struct multi_buddy_state, used_mem) == offsetof(struct dymelor_state, used_mem), + "Can't access used memory field in an homogeneous way!"); + +#define model_allocator_state_size(self) ((self)->used_mem) + +extern void model_allocator_lp_init(struct mm_state *self); +extern void model_allocator_lp_fini(struct mm_state *self); +extern void model_allocator_checkpoint_take(struct mm_state *self, array_count_t ref_i); +extern void model_allocator_checkpoint_next_force_full(struct mm_state *self); +extern array_count_t model_allocator_checkpoint_restore(struct mm_state *self, array_count_t ref_i); extern array_count_t model_allocator_fossil_lp_collect(struct mm_state *self, array_count_t tgt_ref_i); diff --git a/src/mm/msg_allocator.c b/src/mm/msg_allocator.c index f33b7ac75..524286e9c 100644 --- a/src/mm/msg_allocator.c +++ b/src/mm/msg_allocator.c @@ -10,9 +10,7 @@ */ #include -#include #include -#include static __thread dyn_array(struct lp_msg *) free_list = {0}; static __thread dyn_array(struct lp_msg *) at_gvt_list = {0}; diff --git a/src/serial/serial.c b/src/serial/serial.c index 83840844d..238ed6999 100644 --- a/src/serial/serial.c +++ b/src/serial/serial.c @@ -38,7 +38,7 @@ static void serial_simulation_init(void) current_lp = lp; lp->termination_t = -1; - model_allocator_lp_init(); + model_allocator_lp_init(&lp->mm_state); lp->rng_ctx = rs_malloc(sizeof(*lp->rng_ctx)); random_lib_lp_init(); lp->state_pointer = NULL; @@ -60,9 +60,10 @@ static void serial_simulation_init(void) static void serial_simulation_fini(void) { for(uint64_t i = 0; i < global_config.lps; ++i) { - current_lp = &lps[i]; - global_config.dispatcher(i, 0, LP_FINI, NULL, 0, lps[i].state_pointer); - model_allocator_lp_fini(); + struct lp_ctx *lp = &lps[i]; + current_lp = lp; + global_config.dispatcher(i, 0, LP_FINI, NULL, 0, lp->state_pointer); + model_allocator_lp_fini(&lp->mm_state); } for(array_count_t i = 0; i < array_count(queue); ++i) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6c47c8333..359906fe4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -41,7 +41,7 @@ test_program(load tests/core/load.c) # Test data structures and subsystems #test_program(msg_queue tests/datatypes/msg_queue.c) test_program(bitmap tests/datatypes/bitmap.c) -test_program(mm tests/mm/buddy.c tests/mm/buddy_hard.c tests/mm/parallel.c tests/mm/main.c) +test_program(mm tests/mm/main.c tests/mm/mm_easy.c tests/mm/mm_hard.c tests/mm/parallel.c) test_program(termination tests/gvt/termination.c) # Test the statistics subsystem diff --git a/test/tests/core/load.c b/test/tests/core/load.c index c959c707d..1fb9dec1c 100644 --- a/test/tests/core/load.c +++ b/test/tests/core/load.c @@ -62,6 +62,11 @@ int main(void) test_xf("CanEnd not set", init_rootsim, &conf); test_xf("Start simulation with no CanEnd", run_rootsim, NULL); + memcpy(&conf, &valid_conf, sizeof(conf)); + conf.mm = 123; + test_xf("Incorrect model allocator choice", init_rootsim, &conf); + test_xf("Start simulation with model allocator choice", run_rootsim, NULL); + memcpy(&conf, &valid_conf, sizeof(conf)); test("Initialization", init_rootsim, &conf); test("Dummy simulation", run_rootsim, NULL); diff --git a/test/tests/integration/phold.c b/test/tests/integration/phold.c index b3be415a2..801fbce4e 100644 --- a/test/tests/integration/phold.c +++ b/test/tests/integration/phold.c @@ -71,6 +71,7 @@ struct simulation_configuration conf = { .gvt_period = 1000, .log_level = LOG_INFO, .stats_file = "phold", + .mm = MM_DYMELOR, .ckpt_interval = 0, .prng_seed = 0, .core_binding = true, diff --git a/test/tests/mm/main.c b/test/tests/mm/main.c index e0b4feb83..b2bad361d 100644 --- a/test/tests/mm/main.c +++ b/test/tests/mm/main.c @@ -20,7 +20,7 @@ int main(void) { log_init(stdout); - test("Testing buddy system", model_allocator_test, NULL); - test("Testing buddy system (hard test)", model_allocator_test_hard, NULL); + test("Testing LP memory allocator", model_allocator_test, NULL); + test("Testing LP memory allocator (hard test)", model_allocator_test_hard, NULL); test("Testing parallel memory operations", parallel_malloc_test, NULL); } diff --git a/test/tests/mm/buddy.c b/test/tests/mm/mm_easy.c similarity index 73% rename from test/tests/mm/buddy.c rename to test/tests/mm/mm_easy.c index 2ac856eab..b68590720 100644 --- a/test/tests/mm/buddy.c +++ b/test/tests/mm/mm_easy.c @@ -13,18 +13,17 @@ #include #include -#include #include #include #define BUDDY_TEST_SEED 0x5E550UL -static int block_size_test(unsigned b_exp) +static int block_size_test(struct mm_state *mm_state, unsigned b_exp) { int errs = 0; unsigned block_size = 1 << b_exp; - unsigned allocations_cnt = 1 << (B_TOTAL_EXP - b_exp); + unsigned allocations_cnt = 1 << (24 - b_exp); test_rng_state b_rng, b_chk; rng_init(&b_rng, BUDDY_TEST_SEED); uint64_t **allocations = malloc(allocations_cnt * sizeof(uint64_t *)); @@ -43,8 +42,8 @@ static int block_size_test(unsigned b_exp) } } - model_allocator_checkpoint_next_force_full(); - model_allocator_checkpoint_take(0); + model_allocator_checkpoint_next_force_full(mm_state); + model_allocator_checkpoint_take(mm_state, 0); b_chk = b_rng; for(unsigned i = 0; i < allocations_cnt; ++i) { @@ -54,7 +53,7 @@ static int block_size_test(unsigned b_exp) } } - model_allocator_checkpoint_take(1); + model_allocator_checkpoint_take(mm_state, 1); for(unsigned i = 0; i < allocations_cnt; ++i) { for(unsigned j = 0; j < block_size / sizeof(uint64_t); ++j) { @@ -63,9 +62,9 @@ static int block_size_test(unsigned b_exp) } } - model_allocator_checkpoint_take(2); + model_allocator_checkpoint_take(mm_state, 2); - model_allocator_checkpoint_restore(1); + model_allocator_checkpoint_restore(mm_state, 1); b_rng = b_chk; for(unsigned i = 0; i < allocations_cnt; ++i) { @@ -76,7 +75,7 @@ static int block_size_test(unsigned b_exp) rs_free(allocations[i]); } - model_allocator_checkpoint_restore(0); + model_allocator_checkpoint_restore(mm_state, 0); rng_init(&b_rng, BUDDY_TEST_SEED); for(unsigned i = 0; i < allocations_cnt; ++i) { @@ -91,15 +90,11 @@ static int block_size_test(unsigned b_exp) return errs > 0; } -int model_allocator_test(_unused void *_) +static int blocks_full_test(void) { int errs = 0; - - current_lp = test_lp_mock_get(); - model_allocator_lp_init(); - - for(unsigned j = B_BLOCK_EXP; j < B_TOTAL_EXP; ++j) { - errs += block_size_test(j); + for(unsigned j = 6; j < 16; ++j) { + errs += block_size_test(¤t_lp->mm_state, j); } errs += rs_malloc(0) != NULL; @@ -110,8 +105,19 @@ int model_allocator_test(_unused void *_) int64_t *mem = rs_calloc(1, sizeof(uint64_t)); errs += *mem != 0; rs_free(mem); + return errs; +} - model_allocator_lp_fini(); +int model_allocator_test(_unused void *_) +{ + int errs = 0; + current_lp = test_lp_mock_get(); + for(enum mm_allocator_choice mm = MM_MULTI_BUDDY; mm <= MM_DYMELOR; ++mm) { + global_config.mm = MM_MULTI_BUDDY; + model_allocator_lp_init(¤t_lp->mm_state); + errs += blocks_full_test(); + model_allocator_lp_fini(¤t_lp->mm_state); + } return errs; } diff --git a/test/tests/mm/buddy_hard.c b/test/tests/mm/mm_hard.c similarity index 80% rename from test/tests/mm/buddy_hard.c rename to test/tests/mm/mm_hard.c index 8363d8933..a1d10a27a 100644 --- a/test/tests/mm/buddy_hard.c +++ b/test/tests/mm/mm_hard.c @@ -136,7 +136,7 @@ static bool allocation_check(struct alc *alc, unsigned p) return false; } -static bool allocation_cycle(struct alc *alc, unsigned c, unsigned up, unsigned down) +static bool allocation_cycle(struct mm_state *mm_state, struct alc *alc, unsigned c, unsigned up, unsigned down) { for(unsigned i = c + 1; i <= up; ++i) { allocation_partial_write(alc, i); @@ -144,15 +144,15 @@ static bool allocation_cycle(struct alc *alc, unsigned c, unsigned up, unsigned return true; } if(test_random_double() < FULL_CHK_P) { - model_allocator_checkpoint_next_force_full(); + model_allocator_checkpoint_next_force_full(mm_state); } - model_allocator_checkpoint_take(i); + model_allocator_checkpoint_take(mm_state, i); } up = max(up, c); while(1) { - model_allocator_checkpoint_restore(up); + model_allocator_checkpoint_restore(mm_state, up); if(allocation_check(alc, up)) { return true; @@ -166,19 +166,16 @@ static bool allocation_cycle(struct alc *alc, unsigned c, unsigned up, unsigned up -= s; } - model_allocator_checkpoint_restore(down); + model_allocator_checkpoint_restore(mm_state, down); return allocation_check(alc, down); } -int model_allocator_test_hard(_unused void *_) +static int blocks_hard_full_test(struct mm_state *mm_state) { - current_lp = test_lp_mock_get(); - model_allocator_lp_init(); - struct alc *alc = allocation_all_init(); - model_allocator_checkpoint_next_force_full(); - model_allocator_checkpoint_take(0); + model_allocator_checkpoint_next_force_full(mm_state); + model_allocator_checkpoint_take(mm_state, 0); if(allocation_check(alc, 0)) { return -1; @@ -189,17 +186,28 @@ int model_allocator_test_hard(_unused void *_) unsigned u = test_random_range(MAX_ALLOC_PHASES - 1) + 1; unsigned d = test_random_range(u); - if(allocation_cycle(alc, c, u, d)) { + if(allocation_cycle(mm_state, alc, c, u, d)) { return -1; } c = d; } - model_allocator_checkpoint_restore(0); + model_allocator_checkpoint_restore(mm_state, 0); allocation_all_fini(alc); - model_allocator_lp_fini(); + return 0; +} +int model_allocator_test_hard(_unused void *_) +{ + current_lp = test_lp_mock_get(); + for(enum mm_allocator_choice mm = MM_MULTI_BUDDY; mm <= MM_DYMELOR; ++mm) { + global_config.mm = mm; + model_allocator_lp_init(¤t_lp->mm_state); + if(blocks_hard_full_test(¤t_lp->mm_state) < 0) + return -1; + model_allocator_lp_fini(¤t_lp->mm_state); + } return 0; } diff --git a/test/tests/mm/parallel.c b/test/tests/mm/parallel.c index 7927e5837..6b5aa2b8a 100644 --- a/test/tests/mm/parallel.c +++ b/test/tests/mm/parallel.c @@ -12,7 +12,7 @@ #include #include -#include +#include #include @@ -140,40 +140,35 @@ static void bin_test(struct bin_info *p) } } -int parallel_malloc_test(_unused void *_) +static void bins_full_test(void) { - unsigned i, b, j, actions, action; - - current_lp = test_lp_mock_get(); - model_allocator_lp_init(); - struct bin_info p; - p.size = (1 << (B_BLOCK_EXP + 1)); - p.bins = (1 << B_TOTAL_EXP) / p.size; + p.size = 512; + p.bins = 10000; p.m = malloc(p.bins * sizeof(*p.m)); - for(b = 0; b < p.bins; b++) { + for(unsigned b = 0; b < p.bins; b++) { p.m[b].size = 0; p.m[b].ptr = NULL; if(!test_random_range(2)) bin_alloc(&p.m[b], test_random_range(p.size) + 1, test_random_u()); } - for(i = 0; i <= I_MAX;) { + for(unsigned i = 0; i <= I_MAX;) { bin_test(&p); - actions = test_random_range(ACTIONS_MAX); + unsigned actions = test_random_range(ACTIONS_MAX); - for(j = 0; j < actions; j++) { - b = test_random_range(p.bins); + for(unsigned j = 0; j < actions; j++) { + unsigned b = test_random_range(p.bins); bin_free(&p.m[b]); } i += actions; actions = test_random_range(ACTIONS_MAX); - for(j = 0; j < actions; j++) { - b = test_random_range(p.bins); - action = test_random_u(); + for(unsigned j = 0; j < actions; j++) { + unsigned b = test_random_range(p.bins); + unsigned action = test_random_u(); bin_alloc(&p.m[b], test_random_range(p.size) + 1, action); bin_test(&p); } @@ -181,12 +176,20 @@ int parallel_malloc_test(_unused void *_) i += actions; } - for(b = 0; b < p.bins; b++) + for(unsigned b = 0; b < p.bins; b++) bin_free(&p.m[b]); free(p.m); +} - model_allocator_lp_fini(); - +int parallel_malloc_test(_unused void *_) +{ + current_lp = test_lp_mock_get(); + for(enum mm_allocator_choice mm = MM_MULTI_BUDDY; mm <= MM_DYMELOR; ++mm) { + global_config.mm = mm; + model_allocator_lp_init(¤t_lp->mm_state); + bins_full_test(); + model_allocator_lp_fini(¤t_lp->mm_state); + } return 0; }