a much more simplified version of push_back with reallocf instead of memcpy as it is faster (no copying just resizing the vector (entire point of vector))
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
typedef struct {
|
||||
char* arr;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
} Vec8_t;
|
||||
|
||||
Vec8_t create(const Vec8_t* input) {
|
||||
Vec8_t vec = { .arr = NULL, .size = 0, .capacity = 4 };
|
||||
if (input == NULL) {
|
||||
vec.arr = calloc(vec.capacity, sizeof(char));
|
||||
return vec;
|
||||
}
|
||||
if (input->size > 0) {
|
||||
vec.size = input->size + 1;
|
||||
}
|
||||
if (input->capacity >= vec.capacity) {
|
||||
vec.capacity = 2 * input->capacity;
|
||||
}
|
||||
vec.arr = calloc(vec.capacity, sizeof(char));
|
||||
return vec;
|
||||
}
|
||||
|
||||
void delete(Vec8_t* vec) {
|
||||
if (vec->arr != NULL) {
|
||||
free(vec->arr);
|
||||
}
|
||||
vec->arr = NULL;
|
||||
}
|
||||
|
||||
Vec8_t add_back(Vec8_t* vec, const char val) {
|
||||
if (vec->size < vec->capacity) {
|
||||
vec->arr[vec->size] = val;
|
||||
vec->size++;
|
||||
return *vec;
|
||||
}
|
||||
if (vec->size >= vec->capacity) {
|
||||
Vec8_t nvec = create(vec);
|
||||
if (nvec.arr == NULL) {
|
||||
return *vec;
|
||||
}
|
||||
memcpy(&nvec.arr[0], &vec->arr[0], vec->size * sizeof(char));
|
||||
if (nvec.arr && (nvec.size > 0 || nvec.size > vec->size)) {
|
||||
nvec.arr[vec->size] = val;
|
||||
}
|
||||
free(vec->arr);
|
||||
return nvec;
|
||||
}
|
||||
Vec8_t ret = { .arr = NULL, .capacity = 0, .size = 0 };
|
||||
return ret;
|
||||
}
|
||||
|
||||
void print_rusage(const char* label) {
|
||||
struct rusage ru;
|
||||
getrusage(RUSAGE_SELF, &ru);
|
||||
printf("%s: %.2f MB maxRSS\n", label, ru.ru_maxrss / 1024.0);
|
||||
}
|
||||
|
||||
void force_rusage_reset() {
|
||||
struct rusage start;
|
||||
getrusage(RUSAGE_SELF, &start);
|
||||
}
|
||||
|
||||
int main() {
|
||||
printf("=== MEMORY CRASH + LEAK TEST ===\n\n");
|
||||
|
||||
printf("Initial memory:\n");
|
||||
print_rusage("before");
|
||||
|
||||
printf("\n=== TEST 1: PUSH UNTIL CRASH ===\n\n");
|
||||
|
||||
Vec8_t vec = create(NULL);
|
||||
size_t crashed_at = 0;
|
||||
|
||||
for (size_t i = 0; ; i++) {
|
||||
vec = add_back(&vec, (char)(i % 256));
|
||||
|
||||
if (vec.arr == NULL) {
|
||||
crashed_at = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i % 50000000 == 0 && i > 0) {
|
||||
struct rusage ru;
|
||||
getrusage(RUSAGE_SELF, &ru);
|
||||
printf(" %zu elements: %.2f MB used\n", i, ru.ru_maxrss / 1024.0);
|
||||
}
|
||||
|
||||
if (i >= 4000000000UL) {
|
||||
crashed_at = i;
|
||||
printf(" STOPPED at 4 billion\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct rusage ru;
|
||||
getrusage(RUSAGE_SELF, &ru);
|
||||
|
||||
if (crashed_at > 0) {
|
||||
printf("\nCRASHED at %zu elements\n", crashed_at);
|
||||
} else {
|
||||
printf("\nStopped at %zu elements\n", vec.size);
|
||||
printf("capacity: %zu\n", vec.capacity);
|
||||
}
|
||||
|
||||
printf("memory used: %.2f MB\n", ru.ru_maxrss / 1024.0);
|
||||
|
||||
delete(&vec);
|
||||
|
||||
printf("\nAfter delete:\n");
|
||||
print_rusage("after delete");
|
||||
|
||||
printf("\n=== TEST 2: MULTIPLE VECTORS (LEAK CHECK) ===\n\n");
|
||||
|
||||
size_t num_vectors = 10000;
|
||||
Vec8_t* vectors = calloc(num_vectors, sizeof(Vec8_t));
|
||||
|
||||
printf("Creating %zu vectors...\n", num_vectors);
|
||||
print_rusage("before vectors");
|
||||
|
||||
for (size_t i = 0; i < num_vectors; i++) {
|
||||
vectors[i] = create(NULL);
|
||||
for (size_t j = 0; j < 10000; j++) {
|
||||
vectors[i] = add_back(&vectors[i], (char)(j % 256));
|
||||
}
|
||||
}
|
||||
|
||||
print_rusage("with vectors");
|
||||
printf("Each vector has %zu elements\n", vectors[0].size);
|
||||
|
||||
printf("\nFreeing vectors...\n");
|
||||
for (size_t i = 0; i < num_vectors; i++) {
|
||||
delete(&vectors[i]);
|
||||
}
|
||||
free(vectors);
|
||||
|
||||
print_rusage("after free");
|
||||
|
||||
printf("\n=== TEST 3: CREATE/DELETE CYCLES ===\n\n");
|
||||
|
||||
size_t cycles = 100000;
|
||||
printf("Running %zu create/delete cycles...\n", cycles);
|
||||
print_rusage("before cycles");
|
||||
|
||||
for (size_t c = 0; c < cycles; c++) {
|
||||
Vec8_t v = create(NULL);
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
v = add_back(&v, 'x');
|
||||
}
|
||||
delete(&v);
|
||||
|
||||
if (c % 10000 == 0) {
|
||||
struct rusage ru;
|
||||
getrusage(RUSAGE_SELF, &ru);
|
||||
printf(" cycle %zu: %.2f MB\n", c, ru.ru_maxrss / 1024.0);
|
||||
}
|
||||
}
|
||||
|
||||
print_rusage("after cycles");
|
||||
|
||||
printf("\n=== TEST 4: REALLOC STRESS ===\n\n");
|
||||
|
||||
Vec8_t big = create(NULL);
|
||||
printf("Single vec realloc stress...\n");
|
||||
|
||||
size_t prev_cap = 0;
|
||||
for (size_t i = 0; i < 100000000; i++) {
|
||||
big = add_back(&big, 'x');
|
||||
|
||||
if (big.capacity != prev_cap) {
|
||||
struct rusage ru;
|
||||
getrusage(RUSAGE_SELF, &ru);
|
||||
printf(" cap %zu -> %.2f MB\n", big.capacity, ru.ru_maxrss / 1024.0);
|
||||
prev_cap = big.capacity;
|
||||
}
|
||||
}
|
||||
|
||||
print_rusage("final big vec");
|
||||
printf("size: %zu, capacity: %zu\n", big.size, big.capacity);
|
||||
|
||||
delete(&big);
|
||||
|
||||
printf("\nAfter cleanup:\n");
|
||||
print_rusage("cleanup done");
|
||||
|
||||
printf("\n=== RESULT ===\n");
|
||||
|
||||
struct rusage final;
|
||||
getrusage(RUSAGE_SELF, &final);
|
||||
printf("Peak memory: %.2f MB\n", final.ru_maxrss / 1024.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user