Skip to content

Commit

Permalink
Add realloc function to jmem
Browse files Browse the repository at this point in the history
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 [email protected]
  • Loading branch information
dbatyai committed Aug 6, 2019
1 parent 3b7475b commit 286360a
Show file tree
Hide file tree
Showing 3 changed files with 328 additions and 29 deletions.
246 changes: 217 additions & 29 deletions jerry-core/jmem/jmem-heap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

#if !ENABLED (JERRY_SYSTEM_ALLOCATOR)
/**
* Internal method for freeing a memory block.
* Finds the block in the free block list which preceeds the argument block
*
* @return pointer to the preceeding 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 */
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 */
{
#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;
const jmem_heap_free_t *prev_p;

JMEM_VALGRIND_DEFINED_SPACE (&JERRY_HEAP_CONTEXT (first), sizeof (jmem_heap_free_t));

Expand All @@ -391,37 +382,53 @@ 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));
JMEM_VALGRIND_NOACCESS_SPACE (prev_p, sizeof (jmem_heap_free_t));
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)
{
Expand All @@ -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;

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

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_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) -= size;
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
Expand Down
1 change: 1 addition & 0 deletions jerry-core/jmem/jmem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 286360a

Please sign in to comment.