Skip to content

Commit eff1e3e

Browse files
authored
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.
1 parent 9158d49 commit eff1e3e

2 files changed

Lines changed: 135 additions & 54 deletions

File tree

src/deque.c

Lines changed: 51 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ struct internal_deque {
3434
size_t start_index;
3535
size_t end_index;
3636
size_t block_count;
37+
size_t alloc_block_start;
38+
size_t alloc_block_end;
3739
char **data;
3840
};
3941

@@ -70,7 +72,9 @@ deque deque_init(const size_t data_size)
7072
init->block_size * BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT / 2;
7173
init->end_index = init->start_index;
7274
init->block_count = BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT;
73-
init->data = calloc(init->block_count, sizeof(char *));
75+
init->alloc_block_start = init->start_index / init->block_size;
76+
init->alloc_block_end = init->alloc_block_start;
77+
init->data = malloc(init->block_count * sizeof(char *));
7478
if (!init->data) {
7579
free(init);
7680
return NULL;
@@ -81,8 +85,7 @@ deque deque_init(const size_t data_size)
8185
free(init);
8286
return NULL;
8387
}
84-
memcpy(init->data + init->start_index / init->block_size, &block,
85-
sizeof(char *));
88+
init->data[init->alloc_block_start] = block;
8689
return init;
8790
}
8891

@@ -134,20 +137,18 @@ bk_err deque_trim(deque me)
134137
}
135138
memcpy(updated_data, me->data + start_block_index,
136139
updated_block_count * sizeof(char *));
137-
for (i = 0; i < start_block_index; i++) {
138-
char *block;
139-
memcpy(&block, me->data + i, sizeof(char *));
140-
free(block);
140+
for (i = me->alloc_block_start; i < start_block_index; i++) {
141+
free(me->data[i]);
141142
}
142-
for (i = end_block_index + 1; i < me->block_count; i++) {
143-
char *block;
144-
memcpy(&block, me->data + i, sizeof(char *));
145-
free(block);
143+
for (i = end_block_index + 1; i <= me->alloc_block_end; i++) {
144+
free(me->data[i]);
146145
}
147146
free(me->data);
148147
me->start_index -= start_block_index * me->block_size;
149148
me->end_index -= start_block_index * me->block_size;
150149
me->block_count = updated_block_count;
150+
me->alloc_block_start = 0;
151+
me->alloc_block_end = updated_block_count - 1;
151152
me->data = updated_data;
152153
return BK_OK;
153154
}
@@ -235,18 +236,15 @@ bk_err deque_add_all(deque me, void *const arr, const size_t size)
235236
if (!temp) {
236237
return -BK_ENOMEM;
237238
}
238-
memset(temp + me->block_count, 0, appended_blocks * sizeof(char *));
239239
me->data = temp;
240240
me->block_count = new_block_count;
241241
}
242-
for (i = block_index + 1; i <= block_index + needed_blocks; i++) {
243-
if (me->data[i]) {
244-
continue;
245-
}
242+
for (i = me->alloc_block_end + 1; i <= block_index + needed_blocks; i++) {
246243
me->data[i] = malloc(me->block_size * me->data_size);
247244
if (!me->data[i]) {
248245
return -BK_ENOMEM;
249246
}
247+
me->alloc_block_end++;
250248
}
251249
offset = first_block_space * me->data_size;
252250
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)
307305
return -BK_ENOMEM;
308306
}
309307
memmove(temp + added_blocks, temp, me->block_count * sizeof(char *));
310-
memset(temp, 0, added_blocks * sizeof(char *));
311308
me->data = temp;
312309
me->block_count = new_block_count;
313310
me->start_index += added_blocks * me->block_size;
314311
me->end_index += added_blocks * me->block_size;
312+
me->alloc_block_start += added_blocks;
313+
me->alloc_block_end += added_blocks;
315314
}
316315
if (me->start_index % me->block_size == 0) {
317-
char *block;
318-
const size_t previous_block_index =
319-
me->start_index / me->block_size - 1;
320-
memcpy(&block, me->data + previous_block_index, sizeof(char *));
321-
if (!block) {
322-
block = malloc(me->block_size * me->data_size);
323-
if (!block) {
324-
return -BK_ENOMEM;
316+
const size_t add_block_index = me->start_index / me->block_size - 1;
317+
if (add_block_index < me->alloc_block_start) {
318+
const size_t end_block = (me->end_index - 1) / me->block_size;
319+
if (end_block < me->alloc_block_end) {
320+
me->data[add_block_index] = me->data[me->alloc_block_end];
321+
me->alloc_block_end--;
322+
} else {
323+
me->data[add_block_index] =
324+
malloc(me->block_size * me->data_size);
325+
if (!me->data[add_block_index]) {
326+
return -BK_ENOMEM;
327+
}
325328
}
326-
memcpy(me->data + previous_block_index, &block, sizeof(char *));
329+
me->alloc_block_start--;
327330
}
328331
}
329332
me->start_index--;
@@ -355,30 +358,32 @@ bk_err deque_push_back(deque me, void *const data)
355358
{
356359
if (me->end_index == me->block_count * me->block_size) {
357360
const size_t new_block_count = deque_get_new_block_count(me);
358-
size_t added_blocks;
359361
char **temp;
360362
if (new_block_count == 0) {
361363
return -BK_ERANGE;
362364
}
363-
added_blocks = new_block_count - me->block_count;
364365
temp = realloc(me->data, new_block_count * sizeof(char *));
365366
if (!temp) {
366367
return -BK_ENOMEM;
367368
}
368-
memset(temp + me->block_count, 0, added_blocks * sizeof(char *));
369369
me->data = temp;
370370
me->block_count = new_block_count;
371371
}
372372
if (me->end_index % me->block_size == 0) {
373-
char *block;
374-
const size_t tentative_block_index = me->end_index / me->block_size;
375-
memcpy(&block, me->data + tentative_block_index, sizeof(char *));
376-
if (!block) {
377-
block = malloc(me->block_size * me->data_size);
378-
if (!block) {
379-
return -BK_ENOMEM;
373+
const size_t add_block_index = me->end_index / me->block_size;
374+
if (add_block_index > me->alloc_block_end) {
375+
const size_t start_block = me->start_index / me->block_size;
376+
if (start_block > me->alloc_block_start) {
377+
me->data[add_block_index] = me->data[me->alloc_block_start];
378+
me->alloc_block_start++;
379+
} else {
380+
me->data[add_block_index] =
381+
malloc(me->block_size * me->data_size);
382+
if (!me->data[add_block_index]) {
383+
return -BK_ENOMEM;
384+
}
380385
}
381-
memcpy(me->data + tentative_block_index, &block, sizeof(char *));
386+
me->alloc_block_end++;
382387
}
383388
}
384389
{
@@ -586,8 +591,8 @@ bk_err deque_clear(deque me)
586591
{
587592
size_t i;
588593
char *updated_block;
589-
char **updated_data = calloc(BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT,
590-
sizeof(char *));
594+
char **updated_data =
595+
malloc(BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT * sizeof(char *));
591596
if (!updated_data) {
592597
return -BK_ENOMEM;
593598
}
@@ -596,18 +601,17 @@ bk_err deque_clear(deque me)
596601
free(updated_data);
597602
return -BK_ENOMEM;
598603
}
599-
for (i = 0; i < me->block_count; i++) {
600-
char *block;
601-
memcpy(&block, me->data + i, sizeof(char *));
602-
free(block);
604+
for (i = me->alloc_block_start; i <= me->alloc_block_end; i++) {
605+
free(me->data[i]);
603606
}
604607
free(me->data);
605608
me->start_index = me->block_size * BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT / 2;
606609
me->end_index = me->start_index;
607610
me->block_count = BKTHOMPS_DEQUE_INITIAL_BLOCK_COUNT;
611+
me->alloc_block_start = me->start_index / me->block_size;
612+
me->alloc_block_end = me->alloc_block_start;
608613
me->data = updated_data;
609-
memcpy(me->data + me->start_index / me->block_size, &updated_block,
610-
sizeof(char *));
614+
me->data[me->alloc_block_start] = updated_block;
611615
return BK_OK;
612616
}
613617

@@ -622,10 +626,8 @@ bk_err deque_clear(deque me)
622626
deque deque_destroy(deque me)
623627
{
624628
size_t i;
625-
for (i = 0; i < me->block_count; i++) {
626-
char *block;
627-
memcpy(&block, me->data + i, sizeof(char *));
628-
free(block);
629+
for (i = me->alloc_block_start; i <= me->alloc_block_end; i++) {
630+
free(me->data[i]);
629631
}
630632
free(me->data);
631633
free(me);

tst/test_deque.c

Lines changed: 84 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -268,13 +268,14 @@ static void test_large_elements(void)
268268
#if STUB_MALLOC
269269
static void test_init_out_of_memory(void)
270270
{
271-
fail_calloc = 1;
272-
assert(!deque_init(sizeof(int)));
273271
fail_malloc = 1;
274272
assert(!deque_init(sizeof(int)));
275273
fail_malloc = 1;
276274
delay_fail_malloc = 1;
277275
assert(!deque_init(sizeof(int)));
276+
fail_malloc = 1;
277+
delay_fail_malloc = 2;
278+
assert(!deque_init(sizeof(int)));
278279
}
279280
#endif
280281

@@ -342,7 +343,7 @@ static void test_clear_out_of_memory(void)
342343
deque_push_back(me, &i);
343344
}
344345
assert(deque_size(me) == 32);
345-
fail_calloc = 1;
346+
fail_malloc = 1;
346347
assert(deque_clear(me) == -ENOMEM);
347348
for (i = 0; i < 32; i++) {
348349
int get = 0xfacade;
@@ -351,6 +352,7 @@ static void test_clear_out_of_memory(void)
351352
}
352353
assert(deque_size(me) == 32);
353354
fail_malloc = 1;
355+
delay_fail_malloc = 1;
354356
assert(deque_clear(me) == -ENOMEM);
355357
for (i = 0; i < 32; i++) {
356358
int get = 0xfacade;
@@ -461,7 +463,7 @@ static void test_big_object(void)
461463
assert(!deque_destroy(me));
462464
}
463465

464-
void test_add_all(int big_arr_size)
466+
static void test_add_all(int big_arr_size)
465467
{
466468
int i;
467469
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)
489491
free(big_array);
490492
}
491493

492-
void test_add_all_failure(void)
494+
static void test_add_all_failure(void)
493495
{
494496
const size_t big_arr_size = 2000;
495497
size_t i;
@@ -529,6 +531,80 @@ void test_add_all_failure(void)
529531
free(big_array);
530532
}
531533

534+
static void test_block_reuse_forwards(void)
535+
{
536+
size_t i;
537+
size_t queue_size = 1500;
538+
deque queue = deque_init(sizeof(double));
539+
for (i = 0; i < queue_size; i++) {
540+
double d = i;
541+
assert(deque_push_back(queue, &d) == BK_OK);
542+
}
543+
for (i = 0; i < queue_size; i++) {
544+
double d = i;
545+
double get;
546+
assert(deque_push_back(queue, &d) == BK_OK);
547+
assert(deque_pop_front(&get, queue) == BK_OK);
548+
assert(get == d);
549+
}
550+
deque_destroy(queue);
551+
}
552+
553+
static void test_block_reuse_backwards(void)
554+
{
555+
size_t i;
556+
size_t queue_size = 1500;
557+
deque queue = deque_init(sizeof(double));
558+
for (i = 0; i < queue_size; i++) {
559+
double d = i;
560+
assert(deque_push_front(queue, &d) == BK_OK);
561+
}
562+
for (i = 0; i < queue_size; i++) {
563+
double d = i;
564+
double get;
565+
assert(deque_push_front(queue, &d) == BK_OK);
566+
assert(deque_pop_back(&get, queue) == BK_OK);
567+
assert(get == d);
568+
}
569+
deque_destroy(queue);
570+
}
571+
572+
static void test_trim_both_sides(void)
573+
{
574+
int i;
575+
deque me = deque_init(sizeof(int));
576+
for (i = 999; i >= 0; i--) {
577+
assert(deque_push_front(me, &i) == BK_OK);
578+
assert(deque_push_back(me, &i) == BK_OK);
579+
}
580+
assert(deque_size(me) == 2000);
581+
assert(deque_trim(me) == BK_OK);
582+
assert(deque_size(me) == 2000);
583+
for (i = 0; i < 500; i++) {
584+
int get = 0xfacade;
585+
assert(deque_pop_front(&get, me) == BK_OK);
586+
assert(get == i);
587+
get = 0xfacade;
588+
assert(deque_pop_back(&get, me) == BK_OK);
589+
assert(get == i);
590+
}
591+
assert(deque_size(me) == 1000);
592+
assert(deque_trim(me) == BK_OK);
593+
assert(deque_size(me) == 1000);
594+
for (i = 500; i < 1000; i++) {
595+
int get = 0xfacade;
596+
assert(deque_pop_front(&get, me) == BK_OK);
597+
assert(get == i);
598+
get = 0xfacade;
599+
assert(deque_pop_back(&get, me) == BK_OK);
600+
assert(get == i);
601+
}
602+
assert(deque_size(me) == 0);
603+
assert(deque_trim(me) == BK_OK);
604+
assert(deque_size(me) == 0);
605+
deque_destroy(me);
606+
}
607+
532608
void test_deque(void)
533609
{
534610
int i;
@@ -555,4 +631,7 @@ void test_deque(void)
555631
test_add_all(i);
556632
}
557633
test_add_all_failure();
634+
test_block_reuse_forwards();
635+
test_block_reuse_backwards();
636+
test_trim_both_sides();
558637
}

0 commit comments

Comments
 (0)