Skip to content

Commit 05945e4

Browse files
committed
Speed up malloc/free routines
This commit improves malloc() and free() in lib/c.c to eliminate an unnecessary member from a structure and reduce execution time. First, the member 'ptr' is removed from 'chunk_t'. Originally, it was used to return the starting address of a usable memory region when calling malloc(). However, consider the following code and illustration. typedef struct chunk { struct chunk *next; struct chunk *prev; int size; } chunk_t; chunk_t *chunk = ... |<--- usable memory --->| +------+------+------+-------- ... ----------+ | next | prev | size | : | +------+------+------+-------- ... ----------+ ^ ^ | | chunk (chunk + 1) the starting address returned by malloc(). After removing 'ptr' from 'chunk_t', the starting address of the usable memory region can be obtained by adding 1 to a pointer to 'chunk_t'. Thus, 'ptr' becomes unnecessary, so this member is removed from the structure and malloc() returns the starting address by the mentioned approach. Second, for the free() function, after removing 'ptr' from 'chunk_t', the least significant bit of the member 'size' is used to record a flag and prevent traversing the entire allocation list. The LSB of 'size' is used to check whether a chunk is freed or not, and it is also used to speed up the free() function. If a chunk is in the freelist, the LSB will be set to 1 to indicate that the chunk is freed. Otherwise, the chunk is still in the allocation list and usable when the LSB is 0, and the memory region can be used by the caller who calls malloc(). Next, when calling free(), the address of a chunk can be obtained by moving a pointer which is the parameter of free() backward by sizeof(chunk_t) bytes directly. <--- usable memory ---> +------+------+------+------- ... -------------+ | next | prev | size | : | +------+------+------+------- ... -------------+ ^ ^ | | chunk ptr |<------------------>| sizeof(chunk_t) bytes Eventually, it is unnecessary to traverse the allocation list. The address of a chunk can be obtained by moving the given pointer backward, and the LSB of 'size' can be used to validate the chunk, which can then be moved to the freelist. Close #139
1 parent 7e49741 commit 05945e4

File tree

4 files changed

+38
-24
lines changed

4 files changed

+38
-24
lines changed

lib/c.c

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -529,13 +529,27 @@ int fputc(int c, FILE *stream)
529529
/* Non-portable: Assume page size is 4KiB */
530530
#define PAGESIZE 4096
531531

532+
#define CHUNK_SIZE_FREED_MASK 1
533+
#define CHUNK_SIZE_SZ_MASK 0xFFFFFFFE
534+
#define CHUNK_GET_SIZE(size) (size & CHUNK_SIZE_SZ_MASK)
535+
#define IS_CHUNK_GET_FREED(size) (size & CHUNK_SIZE_FREED_MASK)
536+
532537
typedef struct chunk {
533538
struct chunk *next;
534539
struct chunk *prev;
535540
int size;
536-
void *ptr;
537541
} chunk_t;
538542

543+
void chunk_set_freed(chunk_t *chunk)
544+
{
545+
chunk->size |= CHUNK_SIZE_FREED_MASK;
546+
}
547+
548+
void chunk_clear_freed(chunk_t *chunk)
549+
{
550+
chunk->size &= CHUNK_SIZE_SZ_MASK;
551+
}
552+
539553
int __align_up(int size)
540554
{
541555
int mask = PAGESIZE - 1;
@@ -562,7 +576,6 @@ void *malloc(int size)
562576
__alloc_tail = tmp;
563577
__alloc_head->next = NULL;
564578
__alloc_head->prev = NULL;
565-
__alloc_head->ptr = NULL;
566579
__alloc_head->size = 0;
567580
}
568581

@@ -573,7 +586,6 @@ void *malloc(int size)
573586
__freelist_head = tmp;
574587
__freelist_head->next = NULL;
575588
__freelist_head->prev = NULL;
576-
__freelist_head->ptr = NULL;
577589
__freelist_head->size = -1;
578590
}
579591

@@ -591,15 +603,16 @@ void *malloc(int size)
591603
int bsize = 0;
592604

593605
for (chunk_t *fh = __freelist_head; fh->next; fh = fh->next) {
594-
if (fh->size >= size && !best_fit_chunk) {
606+
int fh_size = CHUNK_GET_SIZE(fh->size);
607+
if (fh_size >= size && !best_fit_chunk) {
595608
/* first time setting fh as best_fit_chunk */
596609
best_fit_chunk = fh;
597-
bsize = fh->size;
598-
} else if ((fh->size >= size) && best_fit_chunk &&
599-
(fh->size < bsize)) {
610+
bsize = fh_size;
611+
} else if ((fh_size >= size) && best_fit_chunk &&
612+
(fh_size < bsize)) {
600613
/* If there is a smaller chunk available, replace it. */
601614
best_fit_chunk = fh;
602-
bsize = fh->size;
615+
bsize = fh_size;
603616
}
604617
}
605618

@@ -633,9 +646,9 @@ void *malloc(int size)
633646
__alloc_tail = allocated;
634647
__alloc_tail->next = NULL;
635648
__alloc_tail->size = allocated->size;
636-
int offset = sizeof(chunk_t) - 4;
637-
__alloc_tail->ptr = __alloc_tail + offset;
638-
return __alloc_tail->ptr;
649+
chunk_clear_freed(__alloc_tail);
650+
void *ptr = __alloc_tail + 1;
651+
return ptr;
639652
}
640653

641654
void *calloc(int n, int size)
@@ -660,14 +673,16 @@ int __free_all()
660673

661674
chunk_t *cur = __freelist_head;
662675
chunk_t *rel;
676+
int size;
663677

664678
/* release freelist */
665679
while (cur->next) {
666680
rel = cur;
667681
cur = cur->next;
668682
rel->next = NULL;
669683
rel->prev = NULL;
670-
__rfree(rel, rel->size);
684+
size = CHUNK_GET_SIZE(rel->size);
685+
__rfree(rel, size);
671686
}
672687

673688
if (__alloc_head->next) {
@@ -678,7 +693,8 @@ int __free_all()
678693
cur = cur->next;
679694
rel->next = NULL;
680695
rel->prev = NULL;
681-
__rfree(rel, rel->size);
696+
size = CHUNK_GET_SIZE(rel->size);
697+
__rfree(rel, size);
682698
}
683699
}
684700
return 0;
@@ -689,14 +705,11 @@ void free(void *ptr)
689705
if (!ptr)
690706
return;
691707

692-
/* FIXME: it takes long time to search in chuncks */
693-
chunk_t *cur = __alloc_head;
694-
while (cur->ptr != ptr) {
695-
cur = cur->next;
696-
if (!cur) {
697-
printf("free(): double free detected\n");
698-
abort();
699-
}
708+
char *__ptr = ptr;
709+
chunk_t *cur = __ptr - sizeof(chunk_t);
710+
if (IS_CHUNK_GET_FREED(cur->size)) {
711+
printf("free(): double free detected\n");
712+
abort();
700713
}
701714

702715
chunk_t *prev;
@@ -717,6 +730,7 @@ void free(void *ptr)
717730
/* Insert head in __freelist_head */
718731
cur->next = __freelist_head;
719732
cur->prev = NULL;
733+
chunk_set_freed(cur);
720734
__freelist_head->prev = cur;
721735
__freelist_head = cur;
722736
}

src/defs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#define MAX_VAR_LEN 32
1515
#define MAX_TYPE_LEN 32
1616
#define MAX_PARAMS 8
17-
#define MAX_LOCALS 1450
17+
#define MAX_LOCALS 1500
1818
#define MAX_FIELDS 32
1919
#define MAX_FUNCS 512
2020
#define MAX_FUNC_TRIES 2160

tests/snapshots/fib.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

tests/snapshots/hello.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)