Optimize deque block allocations (#122)

Rather than allocating a new block every time it is needed, it will
check the other end of the outer array to see if blocks are available
but not in use. This way, if a deque is being used like a queue, the
block count will not arbitrarily grow.
This commit is contained in:
Bailey Thompson
2020-08-20 21:19:41 -04:00
committed by GitHub
parent 9158d49e53
commit eff1e3e59a
2 changed files with 135 additions and 54 deletions

View File

@@ -34,6 +34,8 @@ struct internal_deque {
size_t start_index;
size_t end_index;
size_t block_count;
size_t alloc_block_start;
size_t alloc_block_end;
char **data;
};
@@ -70,7 +72,9 @@ deque deque_init(const size_t data_size)
init->block_size * BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT / 2;
init->end_index = init->start_index;
init->block_count = BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT;
init->data = calloc(init->block_count, sizeof(char *));
init->alloc_block_start = init->start_index / init->block_size;
init->alloc_block_end = init->alloc_block_start;
init->data = malloc(init->block_count * sizeof(char *));
if (!init->data) {
free(init);
return NULL;
@@ -81,8 +85,7 @@ deque deque_init(const size_t data_size)
free(init);
return NULL;
}
memcpy(init->data + init->start_index / init->block_size, &block,
sizeof(char *));
init->data[init->alloc_block_start] = block;
return init;
}
@@ -134,20 +137,18 @@ bk_err deque_trim(deque me)
}
memcpy(updated_data, me->data + start_block_index,
updated_block_count * sizeof(char *));
for (i = 0; i < start_block_index; i++) {
char *block;
memcpy(&block, me->data + i, sizeof(char *));
free(block);
for (i = me->alloc_block_start; i < start_block_index; i++) {
free(me->data[i]);
}
for (i = end_block_index + 1; i < me->block_count; i++) {
char *block;
memcpy(&block, me->data + i, sizeof(char *));
free(block);
for (i = end_block_index + 1; i <= me->alloc_block_end; i++) {
free(me->data[i]);
}
free(me->data);
me->start_index -= start_block_index * me->block_size;
me->end_index -= start_block_index * me->block_size;
me->block_count = updated_block_count;
me->alloc_block_start = 0;
me->alloc_block_end = updated_block_count - 1;
me->data = updated_data;
return BK_OK;
}
@@ -235,18 +236,15 @@ bk_err deque_add_all(deque me, void *const arr, const size_t size)
if (!temp) {
return -BK_ENOMEM;
}
memset(temp + me->block_count, 0, appended_blocks * sizeof(char *));
me->data = temp;
me->block_count = new_block_count;
}
for (i = block_index + 1; i <= block_index + needed_blocks; i++) {
if (me->data[i]) {
continue;
}
for (i = me->alloc_block_end + 1; i <= block_index + needed_blocks; i++) {
me->data[i] = malloc(me->block_size * me->data_size);
if (!me->data[i]) {
return -BK_ENOMEM;
}
me->alloc_block_end++;
}
offset = first_block_space * me->data_size;
memcpy(me->data[block_index] + inner_index * me->data_size, arr, offset);
@@ -307,23 +305,28 @@ bk_err deque_push_front(deque me, void *const data)
return -BK_ENOMEM;
}
memmove(temp + added_blocks, temp, me->block_count * sizeof(char *));
memset(temp, 0, added_blocks * sizeof(char *));
me->data = temp;
me->block_count = new_block_count;
me->start_index += added_blocks * me->block_size;
me->end_index += added_blocks * me->block_size;
me->alloc_block_start += added_blocks;
me->alloc_block_end += added_blocks;
}
if (me->start_index % me->block_size == 0) {
char *block;
const size_t previous_block_index =
me->start_index / me->block_size - 1;
memcpy(&block, me->data + previous_block_index, sizeof(char *));
if (!block) {
block = malloc(me->block_size * me->data_size);
if (!block) {
return -BK_ENOMEM;
const size_t add_block_index = me->start_index / me->block_size - 1;
if (add_block_index < me->alloc_block_start) {
const size_t end_block = (me->end_index - 1) / me->block_size;
if (end_block < me->alloc_block_end) {
me->data[add_block_index] = me->data[me->alloc_block_end];
me->alloc_block_end--;
} else {
me->data[add_block_index] =
malloc(me->block_size * me->data_size);
if (!me->data[add_block_index]) {
return -BK_ENOMEM;
}
}
memcpy(me->data + previous_block_index, &block, sizeof(char *));
me->alloc_block_start--;
}
}
me->start_index--;
@@ -355,30 +358,32 @@ bk_err deque_push_back(deque me, void *const data)
{
if (me->end_index == me->block_count * me->block_size) {
const size_t new_block_count = deque_get_new_block_count(me);
size_t added_blocks;
char **temp;
if (new_block_count == 0) {
return -BK_ERANGE;
}
added_blocks = new_block_count - me->block_count;
temp = realloc(me->data, new_block_count * sizeof(char *));
if (!temp) {
return -BK_ENOMEM;
}
memset(temp + me->block_count, 0, added_blocks * sizeof(char *));
me->data = temp;
me->block_count = new_block_count;
}
if (me->end_index % me->block_size == 0) {
char *block;
const size_t tentative_block_index = me->end_index / me->block_size;
memcpy(&block, me->data + tentative_block_index, sizeof(char *));
if (!block) {
block = malloc(me->block_size * me->data_size);
if (!block) {
return -BK_ENOMEM;
const size_t add_block_index = me->end_index / me->block_size;
if (add_block_index > me->alloc_block_end) {
const size_t start_block = me->start_index / me->block_size;
if (start_block > me->alloc_block_start) {
me->data[add_block_index] = me->data[me->alloc_block_start];
me->alloc_block_start++;
} else {
me->data[add_block_index] =
malloc(me->block_size * me->data_size);
if (!me->data[add_block_index]) {
return -BK_ENOMEM;
}
}
memcpy(me->data + tentative_block_index, &block, sizeof(char *));
me->alloc_block_end++;
}
}
{
@@ -586,8 +591,8 @@ bk_err deque_clear(deque me)
{
size_t i;
char *updated_block;
char **updated_data = calloc(BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT,
sizeof(char *));
char **updated_data =
malloc(BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT * sizeof(char *));
if (!updated_data) {
return -BK_ENOMEM;
}
@@ -596,18 +601,17 @@ bk_err deque_clear(deque me)
free(updated_data);
return -BK_ENOMEM;
}
for (i = 0; i < me->block_count; i++) {
char *block;
memcpy(&block, me->data + i, sizeof(char *));
free(block);
for (i = me->alloc_block_start; i <= me->alloc_block_end; i++) {
free(me->data[i]);
}
free(me->data);
me->start_index = me->block_size * BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT / 2;
me->end_index = me->start_index;
me->block_count = BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT;
me->alloc_block_start = me->start_index / me->block_size;
me->alloc_block_end = me->alloc_block_start;
me->data = updated_data;
memcpy(me->data + me->start_index / me->block_size, &updated_block,
sizeof(char *));
me->data[me->alloc_block_start] = updated_block;
return BK_OK;
}
@@ -622,10 +626,8 @@ bk_err deque_clear(deque me)
deque deque_destroy(deque me)
{
size_t i;
for (i = 0; i < me->block_count; i++) {
char *block;
memcpy(&block, me->data + i, sizeof(char *));
free(block);
for (i = me->alloc_block_start; i <= me->alloc_block_end; i++) {
free(me->data[i]);
}
free(me->data);
free(me);

View File

@@ -268,13 +268,14 @@ static void test_large_elements(void)
#if STUB_MALLOC
static void test_init_out_of_memory(void)
{
fail_calloc = 1;
assert(!deque_init(sizeof(int)));
fail_malloc = 1;
assert(!deque_init(sizeof(int)));
fail_malloc = 1;
delay_fail_malloc = 1;
assert(!deque_init(sizeof(int)));
fail_malloc = 1;
delay_fail_malloc = 2;
assert(!deque_init(sizeof(int)));
}
#endif
@@ -342,7 +343,7 @@ static void test_clear_out_of_memory(void)
deque_push_back(me, &i);
}
assert(deque_size(me) == 32);
fail_calloc = 1;
fail_malloc = 1;
assert(deque_clear(me) == -ENOMEM);
for (i = 0; i < 32; i++) {
int get = 0xfacade;
@@ -351,6 +352,7 @@ static void test_clear_out_of_memory(void)
}
assert(deque_size(me) == 32);
fail_malloc = 1;
delay_fail_malloc = 1;
assert(deque_clear(me) == -ENOMEM);
for (i = 0; i < 32; i++) {
int get = 0xfacade;
@@ -461,7 +463,7 @@ static void test_big_object(void)
assert(!deque_destroy(me));
}
void test_add_all(int big_arr_size)
static void test_add_all(int big_arr_size)
{
int i;
double small_array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
@@ -489,7 +491,7 @@ void test_add_all(int big_arr_size)
free(big_array);
}
void test_add_all_failure(void)
static void test_add_all_failure(void)
{
const size_t big_arr_size = 2000;
size_t i;
@@ -529,6 +531,80 @@ void test_add_all_failure(void)
free(big_array);
}
static void test_block_reuse_forwards(void)
{
size_t i;
size_t queue_size = 1500;
deque queue = deque_init(sizeof(double));
for (i = 0; i < queue_size; i++) {
double d = i;
assert(deque_push_back(queue, &d) == BK_OK);
}
for (i = 0; i < queue_size; i++) {
double d = i;
double get;
assert(deque_push_back(queue, &d) == BK_OK);
assert(deque_pop_front(&get, queue) == BK_OK);
assert(get == d);
}
deque_destroy(queue);
}
static void test_block_reuse_backwards(void)
{
size_t i;
size_t queue_size = 1500;
deque queue = deque_init(sizeof(double));
for (i = 0; i < queue_size; i++) {
double d = i;
assert(deque_push_front(queue, &d) == BK_OK);
}
for (i = 0; i < queue_size; i++) {
double d = i;
double get;
assert(deque_push_front(queue, &d) == BK_OK);
assert(deque_pop_back(&get, queue) == BK_OK);
assert(get == d);
}
deque_destroy(queue);
}
static void test_trim_both_sides(void)
{
int i;
deque me = deque_init(sizeof(int));
for (i = 999; i >= 0; i--) {
assert(deque_push_front(me, &i) == BK_OK);
assert(deque_push_back(me, &i) == BK_OK);
}
assert(deque_size(me) == 2000);
assert(deque_trim(me) == BK_OK);
assert(deque_size(me) == 2000);
for (i = 0; i < 500; i++) {
int get = 0xfacade;
assert(deque_pop_front(&get, me) == BK_OK);
assert(get == i);
get = 0xfacade;
assert(deque_pop_back(&get, me) == BK_OK);
assert(get == i);
}
assert(deque_size(me) == 1000);
assert(deque_trim(me) == BK_OK);
assert(deque_size(me) == 1000);
for (i = 500; i < 1000; i++) {
int get = 0xfacade;
assert(deque_pop_front(&get, me) == BK_OK);
assert(get == i);
get = 0xfacade;
assert(deque_pop_back(&get, me) == BK_OK);
assert(get == i);
}
assert(deque_size(me) == 0);
assert(deque_trim(me) == BK_OK);
assert(deque_size(me) == 0);
deque_destroy(me);
}
void test_deque(void)
{
int i;
@@ -555,4 +631,7 @@ void test_deque(void)
test_add_all(i);
}
test_add_all_failure();
test_block_reuse_forwards();
test_block_reuse_backwards();
test_trim_both_sides();
}