/* * The MIT License * * Wavefront Alignment Algorithms * Copyright (c) 2017 by Santiago Marco-Sola * * This file is part of Wavefront Alignment Algorithms. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * PROJECT: Wavefront Alignment Algorithms * AUTHOR(S): Santiago Marco-Sola * VERSION: v21.02.15 * DESCRIPTION: Simple managed-memory stack that reduces memory allocation * overheads. Serves memory from large memory segments and frees all memory * requested at once. */ #include "utils/commons.h" #include "mm_stack.h" /* * Debug */ //#define MM_STACK_FORCE_MALLOC /* * Constants */ #define MM_STACK_INITIAL_SEGMENTS 10 #define MM_STACK_INITIAL_MALLOC_REQUESTS 10 #define MM_STACK_INITIAL_STATES 10 /* * Stack state */ typedef struct { uint64_t segment_idx; uint64_t segment_used; uint64_t num_malloc_requests; } mm_stack_state_t; /* * Memory Segments */ typedef struct { uint64_t size; // Total memory available void* memory; // Memory uint64_t used; // Bytes used (offset to memory next free byte) } mm_stack_segment_t; /* * Segments */ mm_stack_segment_t* mm_stack_segment_new( mm_stack_t* const mm_stack) { // Allocate handler mm_stack_segment_t* const segment = (mm_stack_segment_t*) malloc(sizeof(mm_stack_segment_t)); // Memory segment->size = mm_stack->segment_size; segment->memory = malloc(mm_stack->segment_size); segment->used = 0; // Add to segments vector_insert(mm_stack->segments,segment,mm_stack_segment_t*); // Return return segment; } void mm_stack_segment_clear( mm_stack_segment_t* const segment) { segment->used = 0; } void mm_stack_segment_delete( mm_stack_segment_t* const segment) { free(segment->memory); free(segment); } /* * Setup */ mm_stack_t* mm_stack_new( const uint64_t segment_size) { // Allocate handler mm_stack_t* const mm_stack = (mm_stack_t*) malloc(sizeof(mm_stack_t)); // Memory segments mm_stack->segments = vector_new(MM_STACK_INITIAL_SEGMENTS,mm_stack_segment_t*); mm_stack->segment_size = segment_size; #ifndef MM_STACK_FORCE_MALLOC mm_stack_segment_new(mm_stack); #endif mm_stack->current_segment_idx = 0; // Malloc memory mm_stack->malloc_requests = vector_new(MM_STACK_INITIAL_MALLOC_REQUESTS,void*); // Stack states mm_stack->states = vector_new(MM_STACK_INITIAL_STATES,mm_stack_state_t); // Return return mm_stack; } void mm_stack_clear( mm_stack_t* const mm_stack) { // Clear first memory segment and discard the rest mm_stack_segment_t* const segment = *vector_get_elm(mm_stack->segments,0,mm_stack_segment_t*); mm_stack_segment_clear(segment); mm_stack->current_segment_idx = 0; // Free malloc memory VECTOR_ITERATE(mm_stack->malloc_requests,mem_ptr,m,void*) { free(*mem_ptr); } vector_clear(mm_stack->malloc_requests); // Clear states vector_clear(mm_stack->states); } void mm_stack_delete( mm_stack_t* const mm_stack) { // Delete memory segments VECTOR_ITERATE(mm_stack->segments,segment_ptr,p,mm_stack_segment_t*) { mm_stack_segment_delete(*segment_ptr); } vector_delete(mm_stack->segments); // Free malloc memory VECTOR_ITERATE(mm_stack->malloc_requests,mem_ptr,m,void*) { free(*mem_ptr); } vector_delete(mm_stack->malloc_requests); // Clear states vector_delete(mm_stack->states); // Free handler free(mm_stack); } /* * Allocator */ mm_stack_segment_t* mm_stack_fetch_segment( mm_stack_t* const mm_stack, const uint64_t num_bytes) { // Fetch current segment mm_stack_segment_t* const curr_segment = *vector_get_elm(mm_stack->segments,mm_stack->current_segment_idx,mm_stack_segment_t*); // // Check overall segment size // if (num_bytes > curr_segment->size/2) { // Never buy anything you cannot afford twice // return NULL; // Memory request over max-request size // } // Check available segment size if (curr_segment->used + num_bytes <= curr_segment->size) { return curr_segment; } // Check overall segment size if (num_bytes > curr_segment->size) { return NULL; // Memory request over segment size } // Get free segment const uint64_t num_segments = vector_get_used(mm_stack->segments); ++(mm_stack->current_segment_idx); if (mm_stack->current_segment_idx < num_segments) { // Get next segment mm_stack_segment_t* const segment = *vector_get_elm(mm_stack->segments,mm_stack->current_segment_idx,mm_stack_segment_t*); // Clear mm_stack_segment_clear(segment); // Return return segment; } // Add new segment return mm_stack_segment_new(mm_stack); } void* mm_stack_allocate( mm_stack_t* const mm_stack, const uint64_t num_bytes, const bool zero_mem, const uint64_t align_bytes) { // Zero check if (num_bytes == 0) { fprintf(stderr,"MMStack error. Zero bytes requested\n"); exit(1); } // Add payload const uint64_t num_bytes_allocated = num_bytes + align_bytes; // Fetch segment #ifdef MM_STACK_FORCE_MALLOC mm_stack_segment_t* const segment = NULL; // Force malloc memory #else mm_stack_segment_t* const segment = mm_stack_fetch_segment(mm_stack,num_bytes_allocated); #endif // Allocate memory void* memory_base ; if (segment != NULL) { // Segment-memory memory_base = segment->memory + segment->used; if (zero_mem) memset(memory_base,0,num_bytes_allocated); // Set zero segment->used += num_bytes_allocated; // Update segment } else { // Malloc-memory memory_base = malloc(num_bytes_allocated); if (zero_mem) memset(memory_base,0,num_bytes_allocated); // Set zero // Add malloc-request vector_insert(mm_stack->malloc_requests,memory_base,void*); } // Check alignment if (align_bytes == 0) return memory_base; // Align memory request void* memory_aligned = memory_base + align_bytes; memory_aligned = memory_aligned - ((uintptr_t)memory_aligned % align_bytes); return memory_aligned; } /* * Push/pop states */ void mm_stack_push( mm_stack_t* const mm_stack) { // Get new stack-state mm_stack_state_t* stack_state; vector_alloc_new(mm_stack->states,mm_stack_state_t,stack_state); // Store current state mm_stack_segment_t* const current_segment = *vector_get_elm(mm_stack->segments,mm_stack->current_segment_idx,mm_stack_segment_t*); stack_state->segment_idx = mm_stack->current_segment_idx; stack_state->segment_used = current_segment->used; stack_state->num_malloc_requests = vector_get_used(mm_stack->malloc_requests); } void mm_stack_pop( mm_stack_t* const mm_stack) { // Get last stack-state mm_stack_state_t* const stack_state = vector_get_last_elm(mm_stack->states,mm_stack_state_t); vector_dec_used(mm_stack->states); // Restore segment-memory state mm_stack->current_segment_idx = stack_state->segment_idx; mm_stack_segment_t* const current_segment = *(vector_get_elm(mm_stack->segments,stack_state->segment_idx,mm_stack_segment_t*)); current_segment->used = stack_state->segment_used; // Restore malloc-memory state (free requests) const uint64_t total_malloc_requests = vector_get_used(mm_stack->malloc_requests); void** const malloc_requests = vector_get_mem(mm_stack->malloc_requests,void*); uint64_t i; for (i=stack_state->num_malloc_requests;imalloc_requests,stack_state->num_malloc_requests); } /* * Display */ void mm_stack_print( FILE* const stream, mm_stack_t* const mm_stack) { // Print header fprintf(stream,"MMStack.report\n"); // Print segment information const uint64_t num_segments = vector_get_used(mm_stack->segments); const uint64_t segment_size = mm_stack->segment_size; fprintf(stream," => Segments.allocated %" PRIu64 "\n",num_segments); fprintf(stream," => Segments.size %" PRIu64 " MB\n",segment_size/(1024*1024)); fprintf(stream," => Memory.available %" PRIu64 " MB\n",num_segments*(segment_size/(1024*1024))); }