Add realloc function to jmem (#2998)

This patch extends jmem functionality by adding a realloc function.
This opens up other paths of optimization which can result in smaller
peak memory usage and faster execution times, due to not having to
duplicate memory when we need to extend blocks.

JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai@inf.u-szeged.hu
This commit is contained in:
Dániel Bátyai
2019-08-06 17:59:49 +02:00
committed by Robert Fancsik
parent 3b7475b01d
commit 566e81451e
3 changed files with 335 additions and 36 deletions
+224 -36
View File
@@ -353,25 +353,16 @@ jmem_heap_alloc_block_null_on_error (const size_t size) /**< required memory siz
return block_p;
} /* jmem_heap_alloc_block_null_on_error */
/**
* Internal method for freeing a memory block.
*/
void JERRY_ATTR_HOT
jmem_heap_free_block_internal (void *ptr, /**< pointer to beginning of data space of the block */
const size_t size) /**< size of allocated region */
{
#if !ENABLED (JERRY_SYSTEM_ALLOCATOR)
/* checking that ptr points to the heap */
JERRY_ASSERT (jmem_is_heap_pointer (ptr));
JERRY_ASSERT (size > 0);
JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_limit) >= JERRY_CONTEXT (jmem_heap_allocated_size));
JMEM_VALGRIND_FREELIKE_SPACE (ptr);
JMEM_VALGRIND_NOACCESS_SPACE (ptr, size);
jmem_heap_free_t *block_p = (jmem_heap_free_t *) ptr;
jmem_heap_free_t *prev_p;
jmem_heap_free_t *next_p;
/**
* Finds the block in the free block list which preceeds the argument block
*
* @return pointer to the preceeding block
*/
static jmem_heap_free_t *
jmem_heap_find_prev (const jmem_heap_free_t * const block_p) /**< which memory block's predecessor we're looking for */
{
const jmem_heap_free_t *prev_p;
JMEM_VALGRIND_DEFINED_SPACE (&JERRY_HEAP_CONTEXT (first), sizeof (jmem_heap_free_t));
@@ -391,7 +382,7 @@ jmem_heap_free_block_internal (void *ptr, /**< pointer to beginning of data spac
/* Find position of region in the list. */
while (prev_p->next_offset < block_offset)
{
next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset);
const jmem_heap_free_t * const next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset);
JERRY_ASSERT (jmem_is_heap_pointer (next_p));
JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t));
@@ -399,29 +390,45 @@ jmem_heap_free_block_internal (void *ptr, /**< pointer to beginning of data spac
prev_p = next_p;
}
next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset);
JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t));
return (jmem_heap_free_t *) prev_p;
} /* jmem_heap_find_prev */
/* Realign size */
const size_t aligned_size = (size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT;
/**
* Inserts the block into the free chain after a specified block.
*
* Note:
* 'jmem_heap_find_prev' can and should be used to find the previous free block
*/
static void
jmem_heap_insert_block (jmem_heap_free_t *block_p, /**< block to insert */
jmem_heap_free_t *prev_p, /**< the free block after which to insert 'block_p' */
const size_t size) /**< size of the inserted block */
{
JERRY_ASSERT ((uintptr_t) block_p % JMEM_ALIGNMENT == 0);
JERRY_ASSERT (size % JMEM_ALIGNMENT == 0);
JMEM_VALGRIND_DEFINED_SPACE (block_p, sizeof (jmem_heap_free_t));
JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t));
jmem_heap_free_t *next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset);
JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t));
JMEM_VALGRIND_DEFINED_SPACE (block_p, sizeof (jmem_heap_free_t));
const uint32_t block_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (block_p);
/* Update prev. */
if (jmem_heap_get_region_end (prev_p) == block_p)
{
/* Can be merged. */
prev_p->size += (uint32_t) aligned_size;
prev_p->size += (uint32_t) size;
JMEM_VALGRIND_NOACCESS_SPACE (block_p, sizeof (jmem_heap_free_t));
block_p = prev_p;
}
else
{
block_p->size = (uint32_t) aligned_size;
block_p->size = (uint32_t) size;
prev_p->next_offset = block_offset;
}
JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t));
/* Update next. */
if (jmem_heap_get_region_end (block_p) == next_p)
{
@@ -439,28 +446,209 @@ jmem_heap_free_block_internal (void *ptr, /**< pointer to beginning of data spac
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
JMEM_VALGRIND_NOACCESS_SPACE (block_p, size);
JMEM_VALGRIND_NOACCESS_SPACE (next_p, sizeof (jmem_heap_free_t));
} /* jmem_heap_insert_block */
#endif /* !ENABLED (JERRY_SYSTEM_ALLOCATOR) */
/**
* Internal method for freeing a memory block.
*/
void JERRY_ATTR_HOT
jmem_heap_free_block_internal (void *ptr, /**< pointer to beginning of data space of the block */
const size_t size) /**< size of allocated region */
{
JERRY_ASSERT (size > 0);
JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_limit) >= JERRY_CONTEXT (jmem_heap_allocated_size));
JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_allocated_size) > 0);
#if !ENABLED (JERRY_SYSTEM_ALLOCATOR)
/* checking that ptr points to the heap */
JERRY_ASSERT (jmem_is_heap_pointer (ptr));
JERRY_ASSERT ((uintptr_t) ptr % JMEM_ALIGNMENT == 0);
const size_t aligned_size = (size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT;
jmem_heap_free_t * const block_p = (jmem_heap_free_t *) ptr;
jmem_heap_free_t * const prev_p = jmem_heap_find_prev (block_p);
jmem_heap_insert_block (block_p, prev_p, aligned_size);
JERRY_CONTEXT (jmem_heap_allocated_size) -= aligned_size;
while (JERRY_CONTEXT (jmem_heap_allocated_size) + CONFIG_GC_LIMIT <= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) -= CONFIG_GC_LIMIT;
}
JMEM_VALGRIND_NOACCESS_SPACE (&JERRY_HEAP_CONTEXT (first), sizeof (jmem_heap_free_t));
JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_limit) >= JERRY_CONTEXT (jmem_heap_allocated_size));
JMEM_VALGRIND_NOACCESS_SPACE (ptr, size);
JMEM_VALGRIND_FREELIKE_SPACE (ptr);
#else /* ENABLED (JERRY_SYSTEM_ALLOCATOR) */
JERRY_CONTEXT (jmem_heap_allocated_size) -= size;
free (ptr);
#endif /* !ENABLED (JERRY_SYSTEM_ALLOCATOR) */
while (JERRY_CONTEXT (jmem_heap_allocated_size) + CONFIG_GC_LIMIT <= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) -= CONFIG_GC_LIMIT;
}
JERRY_ASSERT (JERRY_CONTEXT (jmem_heap_limit) >= JERRY_CONTEXT (jmem_heap_allocated_size));
} /* jmem_heap_free_block_internal */
/**
* Reallocates the memory region pointed to by 'ptr', changing the size of the allocated region.
*
* @return pointer to the reallocated region
*/
void * JERRY_ATTR_HOT
jmem_heap_realloc_block (void *ptr, /**< memory region to reallocate */
const size_t old_size, /**< current size of the region */
const size_t new_size) /**< desired new size */
{
#if !ENABLED (JERRY_SYSTEM_ALLOCATOR)
JERRY_ASSERT (jmem_is_heap_pointer (ptr));
JERRY_ASSERT ((uintptr_t) ptr % JMEM_ALIGNMENT == 0);
JERRY_ASSERT (old_size != 0);
JERRY_ASSERT (new_size != 0);
jmem_heap_free_t * const block_p = (jmem_heap_free_t *) ptr;
const size_t aligned_new_size = (new_size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT;
const size_t aligned_old_size = (old_size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT;
if (aligned_old_size == aligned_new_size)
{
JMEM_VALGRIND_NOACCESS_SPACE (block_p, old_size);
JMEM_VALGRIND_DEFINED_SPACE (block_p, new_size);
JMEM_HEAP_STAT_FREE (old_size);
JMEM_HEAP_STAT_ALLOC (new_size);
return block_p;
}
jmem_heap_free_t *prev_p = jmem_heap_find_prev (block_p);
if (aligned_new_size < aligned_old_size)
{
JMEM_VALGRIND_NOACCESS_SPACE (block_p, old_size);
JMEM_VALGRIND_DEFINED_SPACE (block_p, new_size);
JMEM_HEAP_STAT_FREE (old_size);
JMEM_HEAP_STAT_ALLOC (new_size);
jmem_heap_insert_block ((jmem_heap_free_t *)((uint8_t *) block_p + aligned_new_size),
prev_p,
aligned_old_size - aligned_new_size);
JERRY_CONTEXT (jmem_heap_allocated_size) -= (aligned_old_size - aligned_new_size);
while (JERRY_CONTEXT (jmem_heap_allocated_size) + CONFIG_GC_LIMIT <= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) -= CONFIG_GC_LIMIT;
}
return block_p;
}
void *ret_block_p = NULL;
const size_t required_size = aligned_new_size - aligned_old_size;
#if !ENABLED (JERRY_MEM_GC_BEFORE_EACH_ALLOC)
if (JERRY_CONTEXT (jmem_heap_allocated_size) + required_size >= JERRY_CONTEXT (jmem_heap_limit))
#endif /* !ENABLED (JERRY_MEM_GC_BEFORE_EACH_ALLOC) */
{
ecma_free_unused_memory (JMEM_PRESSURE_LOW);
}
JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t));
jmem_heap_free_t * const next_p = JMEM_HEAP_GET_ADDR_FROM_OFFSET (prev_p->next_offset);
JMEM_VALGRIND_DEFINED_SPACE (next_p, sizeof (jmem_heap_free_t));
/* Check if block can be extended at the end */
if (((jmem_heap_free_t *) ((uint8_t *) block_p + aligned_old_size)) == next_p)
{
if (required_size <= next_p->size)
{
/* Block can be extended, update the list. */
if (required_size == next_p->size)
{
prev_p->next_offset = next_p->next_offset;
}
else
{
jmem_heap_free_t *const new_next_p = (jmem_heap_free_t *) ((uint8_t *) next_p + required_size);
JMEM_VALGRIND_DEFINED_SPACE (new_next_p, sizeof (jmem_heap_free_t));
new_next_p->next_offset = next_p->next_offset;
new_next_p->size = (uint32_t) (next_p->size - required_size);
JMEM_VALGRIND_NOACCESS_SPACE (new_next_p, sizeof (jmem_heap_free_t));
prev_p->next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (new_next_p);
}
JMEM_VALGRIND_NOACCESS_SPACE ((uint8_t *) block_p, old_size);
JMEM_VALGRIND_DEFINED_SPACE ((uint8_t *) block_p, new_size);
ret_block_p = block_p;
}
}
/*
* Check if block can be extended at the front.
* This is less optimal because we need to copy the data, but still better than allocting a new block.
*/
else if (jmem_heap_get_region_end (prev_p) == block_p)
{
if (required_size <= prev_p->size)
{
if (required_size == prev_p->size)
{
prev_p = jmem_heap_find_prev (prev_p);
JMEM_VALGRIND_DEFINED_SPACE (prev_p, sizeof (jmem_heap_free_t));
prev_p->next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (next_p);
}
else
{
prev_p->size = (uint32_t) (prev_p->size - required_size);
}
ret_block_p = (uint8_t *) block_p - required_size;
memmove (ret_block_p, block_p, old_size);
JMEM_VALGRIND_NOACCESS_SPACE ((uint8_t *) block_p, old_size);
JMEM_VALGRIND_DEFINED_SPACE ((uint8_t *) ret_block_p, new_size);
}
}
if (ret_block_p != NULL)
{
/* Managed to extend the block. Update memory usage and the skip pointer. */
JERRY_CONTEXT (jmem_heap_list_skip_p) = prev_p;
JERRY_CONTEXT (jmem_heap_allocated_size) += required_size;
while (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT;
}
}
else
{
/* Could not extend block. Allocate new region and copy the data. */
/* jmem_heap_alloc_block_internal will adjust the allocated_size, but insert_block will not,
so we reduce it here first, so that the limit calculation remains consistent. */
JERRY_CONTEXT (jmem_heap_allocated_size) -= aligned_old_size;
ret_block_p = jmem_heap_alloc_block_internal (new_size);
memcpy (ret_block_p, block_p, old_size);
jmem_heap_insert_block (block_p, prev_p, aligned_old_size);
}
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
JMEM_VALGRIND_NOACCESS_SPACE (next_p, sizeof (jmem_heap_free_t));
JMEM_HEAP_STAT_FREE (old_size);
JMEM_HEAP_STAT_ALLOC (new_size);
return ret_block_p;
#else /* ENABLED (JERRY_SYSTEM_ALLOCATOR) */
JERRY_CONTEXT (jmem_heap_allocated_size) += (new_size - old_size);
while (JERRY_CONTEXT (jmem_heap_allocated_size) >= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) += CONFIG_GC_LIMIT;
}
while (JERRY_CONTEXT (jmem_heap_allocated_size) + CONFIG_GC_LIMIT <= JERRY_CONTEXT (jmem_heap_limit))
{
JERRY_CONTEXT (jmem_heap_limit) -= CONFIG_GC_LIMIT;
}
free (ptr);
JMEM_HEAP_STAT_FREE (old_size);
JMEM_HEAP_STAT_ALLOC (new_size);
return realloc (ptr, new_size);
#endif /* !ENABLED (JERRY_SYSTEM_ALLOCATOR) */
} /* jmem_heap_free_block_internal */
} /* jmem_heap_realloc_block */
/**
* Free memory block
+1
View File
@@ -108,6 +108,7 @@ void jmem_finalize (void);
void *jmem_heap_alloc_block (const size_t size);
void *jmem_heap_alloc_block_null_on_error (const size_t size);
void *jmem_heap_realloc_block (void *ptr, const size_t old_size, const size_t new_size);
void jmem_heap_free_block (void *ptr, const size_t size);
#if ENABLED (JERRY_MEM_STATS)