In high-performance computing, standard memory allocators like `malloc()` can become a bottleneck. This project demonstrates the ability to solve this by building a custom, efficient memory allocator from the ground up. Such specialized allocators are critical in fields like game development, real-time operating systems, and embedded systems, where predictable performance and low-latency memory management are paramount.
Project Implementation in Brief
This allocator, implemented in C, manages a 4096-byte static memory pool using a header-based, first-fit algorithm. To combat fragmentation, the `myfree()` implementation includes a coalescing mechanism that merges adjacent free blocks, maximizing memory availability for future allocations.
Comprehensive Testing & Stress Analysis
To ensure correctness and robustness, the allocator was subjected to a rigorous testing regimen:
Correctness & Edge Cases:
A suite of targeted tests validates core requirements, as seen in Test 1, Test 4 (Part 2), and Test 5. This suite ensures no memory overlap, proper deallocation, robust error handling, and automated memory leak detection.
Performance & Stress Testing:
The Stress Test (memgrind) program analyzes performance under various workloads. Each demanding workload was executed 50 times, with tests involving up to 120 allocation/deallocation cycles per run to measure efficiency and reliability under stress.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "mymalloc.h"
typedef struct Node { int value; struct Node *next; } Node;
typedef struct Tree { int value; struct Tree *right; struct Tree *left; } Tree;
void freeTree(Tree *root){
if(!root){ return; }
freeTree(root -> left);
freeTree(root -> right);
free(root);
}
int main(){
struct timeval start, end;
long seconds, useconds;
gettimeofday(&start, NULL);
for(int t = 0; t < 50; t++){
for(int i = 0; i < 120; i++){
char *a = malloc(1);
if(a == NULL){ fprintf(stderr, "Memory allocation failed"); continue; }
free(a);
}
}
gettimeofday(&end, NULL);
seconds = end.tv_sec - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
double time = (seconds * 1000000.0 + useconds) / 1000000.0;
double avg_time = time/50;
printf("The average time for first workload is %1.8f seconds.\n", avg_time);
gettimeofday(&start, NULL);
for(int t = 0; t < 50; t++){
char *p[120];
for(int i = 0; i < 120; i++){
p[i] = malloc(1);
if(p[i] == NULL){ fprintf(stderr, "Memory allocation failed"); continue; }
}
for(int i = 0; i < 120; i++){ free(p[i]); }
}
gettimeofday(&end, NULL);
seconds = end.tv_sec - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
double time2 = (seconds * 1000000.0 + useconds) / 1000000.0;
double avg_time2 = time2 / 50;
printf("The average time for second workload is %1.8f seconds.\n", avg_time2);
gettimeofday(&start, NULL);
for(int t = 0; t < 50; t++){
char *q[120];
int alreay_free[120] = {0};
int up = 3, low = 0;
for(int i = 0; i < 120; i++){
q[i] = malloc(1);
if(i == up){
for(int j = low; j < i; j++){
int random = (rand() % (up - 0 + 1)) + 0;
if(alreay_free[random] == 0){ free(q[random]); alreay_free[random] = 1; }
}
int random2 = (rand() % (4 - 1 + 1)) + 1;
low = up;
up += random2;
}
}
for(int i = 0; i < 120; i++){
if(alreay_free[i] == 0){ free(q[i]); }
}
}
gettimeofday(&end, NULL);
seconds = end.tv_sec - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
double time3 = (seconds * 1000000.0 + useconds) / 1000000.0;
double avg_time3 = time3 / 50;
printf("The average time for third workload is %1.8f seconds.\n", avg_time3);
gettimeofday(&start, NULL);
for(int j = 0; j < 50; j++){
Node *head = NULL;
for (int i = 0; i < 100; i++) {
Node* node = (Node*)malloc(sizeof(Node));
node -> value = 10;
node -> next = head;
head = node;
}
while(head != NULL){
Node *next = head -> next;
free(head);
head = next;
}
}
gettimeofday(&end, NULL);
seconds = end.tv_sec - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
double time4 = (seconds * 1000000.0 + useconds) / 1000000.0;
double avg_time4 = time4/50;
printf("The average time for fourth workload is %1.8f seconds.\n", avg_time4);
gettimeofday(&start, NULL);
for(int j = 0; j < 50; j++){
Tree* nodes[100];
for(int i = 0; i < 100; i++){
nodes[i] = malloc(sizeof(Tree));
nodes[i] -> value = i;
nodes[i] -> left = NULL;
nodes[i] -> right = NULL;
}
for(int i = 0; i < 100; i++){
int left = (i * 2) + 1;
int right = (i * 2) + 2;
if(left < 100){ nodes[i] -> left = nodes[left]; }
if(right < 100){ nodes[i] -> right = nodes[right]; }
}
freeTree(nodes[0]);
}
gettimeofday(&end, NULL);
seconds = end.tv_sec - start.tv_sec;
useconds = end.tv_usec - start.tv_usec;
double time5 = (seconds * 1000000.0 + useconds) / 1000000.0;
double avg_time5 = time5/50;
printf("The average time for fifth workload is %1.8f seconds.\n", avg_time5);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#ifndef REALMALLOC
#include "mymalloc.h"
#endif
#ifndef LEAK
#define LEAK 0
#endif
#define MEMSIZE 4096
#define HEADERSIZE 8
#define OBJECTS 64
#define OBJSIZE (MEMSIZE / OBJECTS - HEADERSIZE)
int main(int argc, char **argv)
{
char *obj[OBJECTS];
int i, j, errors = 0;
for (i = 0; i < OBJECTS; i++) {
obj[i] = malloc(OBJSIZE);
if (obj[i] == NULL) {
printf("Unable to allocate object %d\n", i);
exit(1);
}
}
for (i = 0; i < OBJECTS; i++) {
memset(obj[i], i, OBJSIZE);
}
for (i = 0; i < OBJECTS; i++) {
for (j = 0; j < OBJSIZE; j++) {
if (obj[i][j] != i) {
errors++;
printf("Object %d byte %d incorrect: %d\n", i, j, obj[i][j]);
}
}
}
if (!LEAK) {
for (i = 0; i < OBJECTS; i++) {
free(obj[i]);
}
}
printf("%d incorrect bytes\n", errors);
return EXIT_SUCCESS;
}
How to Compile & Run
To compile and run this project on your local machine, clone the repository and use the provided Makefile in your terminal.
bash
make allgcc -c mymalloc.c
gcc -c memgrind.c
gcc memgrind.o mymalloc.o -o memgrind
gcc -c memtest.c
gcc memtest.o mymalloc.o -o memtest
... (compilation for all other tests) ..../memgrindThe average time for first workload is 0.00000214 seconds.
The average time for second workload is 0.00000188 seconds.
The average time for third workload is 0.00000204 seconds.
The average time for fourth workload is 0.00000128 seconds.
The average time for fifth workload is 0.00000196 seconds.make cleanrm -f *.o mymalloc memgrind memtest Test1 Test2 Test3 Test4_1 Test4_2 Test4_3 Test5