perf(allocator): made small allocations reusable by adding a memory bin

This commit is contained in:
Mineplay 2025-04-11 19:01:08 -05:00
parent dd10dfd376
commit 120ddaed02

View file

@ -24,6 +24,8 @@
#if defined(_WIN32) #if defined(_WIN32)
#include <windows.h> #include <windows.h>
static HANDLE hallocy_heap = NULL;
#elif defined(__linux__) #elif defined(__linux__)
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h> #include <sys/mman.h>
@ -35,7 +37,12 @@ typedef struct HallocyMemoryHeader {
struct HallocyMemoryHeader *next; struct HallocyMemoryHeader *next;
} HallocyMemoryHeader; } HallocyMemoryHeader;
static _Thread_local size_t hallocy_small_memory_freed = 0;
static _Thread_local size_t hallocy_small_memory_allocated = 0;
static _Thread_local HallocyMemoryHeader *hallocy_small_memory_bin = NULL;
static size_t page_size = 0; static size_t page_size = 0;
static size_t hallocy_small_allocation_size = 0;
void *hallocy_malloc(size_t size) { void *hallocy_malloc(size_t size) {
if (page_size == 0) { if (page_size == 0) {
@ -45,20 +52,65 @@ void *hallocy_malloc(size_t size) {
page_size = system_info.dwPageSize; page_size = system_info.dwPageSize;
#elif defined(__linux__) #elif defined(__linux__)
page_size = sysconf(_SC_PAGESIZE); page_size = sysconf(_SC_PAGE_SIZE);
#endif #endif
hallocy_small_allocation_size = page_size;
} }
size_t aligned_size = page_size * (((size + sizeof(HallocyMemoryHeader)) / page_size) + 1); size_t aligned_size = page_size * (((size + sizeof(HallocyMemoryHeader)) / page_size) + 1);
HallocyMemoryHeader *memory_pointer = NULL; HallocyMemoryHeader *memory_pointer = NULL;
#if defined(_WIN32) if (aligned_size <= hallocy_small_allocation_size) {
memory_pointer = (HallocyMemoryHeader*)VirtualAlloc(NULL, aligned_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); HallocyMemoryHeader *previous_header = NULL;
#elif defined(__linux__) memory_pointer = hallocy_small_memory_bin;
memory_pointer = (HallocyMemoryHeader*)mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); while (memory_pointer != NULL) {
#endif if (memory_pointer->size >= aligned_size) {
hallocy_small_memory_allocated += memory_pointer->size;
hallocy_small_memory_freed -= memory_pointer->size;
if (memory_pointer == NULL) { if (previous_header == NULL) {
return NULL; hallocy_small_memory_bin = hallocy_small_memory_bin->next;
} else {
previous_header->next = memory_pointer->next;
}
memory_pointer->next = NULL;
return (void*)(memory_pointer + 1);
}
previous_header = memory_pointer;
memory_pointer = memory_pointer->next;
}
#if defined(_WIN32)
if (hallocy_heap == NULL) {
hallocy_heap = GetProcessHeap();
}
memory_pointer = HeapAlloc(hallocy_heap, 0, aligned_size);
if (memory_pointer == NULL) {
return NULL;
}
#elif defined(__linux__)
memory_pointer = mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (memory_pointer == MAP_FAILED) {
return NULL;
}
#endif
hallocy_small_memory_allocated += aligned_size;
} else {
#if defined(_WIN32)
memory_pointer = (HallocyMemoryHeader*)VirtualAlloc(NULL, aligned_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (memory_pointer == NULL) {
return NULL;
}
#elif defined(__linux__)
memory_pointer = (HallocyMemoryHeader*)mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (memory_pointer == MAP_FAILED) {
return NULL;
}
#endif
} }
memory_pointer->size = aligned_size; memory_pointer->size = aligned_size;
@ -73,45 +125,102 @@ HallocyError hallocy_free(void *pointer) {
} }
HallocyMemoryHeader *memory_header = ((HallocyMemoryHeader*)pointer) - 1; HallocyMemoryHeader *memory_header = ((HallocyMemoryHeader*)pointer) - 1;
#if defined(_WIN32) if (memory_header->size <= hallocy_small_allocation_size) {
bool result = VirtualFree(memory_header, 0, MEM_RELEASE); memory_header->next = hallocy_small_memory_bin;
if (result == false) { hallocy_small_memory_bin = memory_header;
DWORD error = GetLastError();
switch (error) { hallocy_small_memory_allocated -= memory_header->size;
case ERROR_INVALID_PARAMETER: hallocy_small_memory_freed += memory_header->size;
return HALLOCY_ERROR_INVALID_PARAM; if (hallocy_small_memory_allocated == 0 && hallocy_small_memory_freed > hallocy_small_allocation_size * 10) {
HallocyMemoryHeader *previous_header = NULL;
HallocyMemoryHeader *current_header = hallocy_small_memory_bin;
while (current_header != NULL) {
previous_header = current_header;
current_header = current_header->next;
case ERROR_NOT_ENOUGH_MEMORY: #if defined(_WIN32)
return HALLOCY_ERROR_OUT_OF_MEMORY; bool result = HeapFree(hallocy_heap, 0, previous_header);
if (result == false) {
DWORD error = GetLastError();
switch (error) {
case ERROR_INVALID_PARAMETER:
return HALLOCY_ERROR_INVALID_PARAM;
case ERROR_NOT_ENOUGH_MEMORY:
return HALLOCY_ERROR_OUT_OF_MEMORY;
case ERROR_INVALID_HANDLE:
return HALLOCY_ERROR_INVALID_POINTER;
default:
return HALLOCY_ERROR_UNKNOWN;
}
}
#elif defined(__linux__)
int result = munmap(previous_header, previous_header->size);
if (result == -1) {
switch (errno) {
case EINVAL:
return HALLOCY_ERROR_INVALID_PARAM;
case ENOMEM:
return HALLOCY_ERROR_OUT_OF_MEMORY;
case EFAULT:
return HALLOCY_ERROR_INVALID_POINTER;
default:
return HALLOCY_ERROR_UNKNOWN;
}
}
#endif
}
case ERROR_INVALID_HANDLE: hallocy_small_memory_bin = NULL;
return HALLOCY_ERROR_INVALID_POINTER; hallocy_small_memory_allocated = 0;
hallocy_small_memory_freed = 0;
case ERROR_INVALID_ADDRESS:
return HALLOCY_ERROR_INVALID_POINTER;
default:
return HALLOCY_ERROR_UNKNOWN;
} }
} } else {
#elif defined(__linux__) #if defined(_WIN32)
int result = munmap(memory_header, memory_header->size); bool result = VirtualFree(memory_header, 0, MEM_RELEASE);
if (result == -1) { if (result == false) {
switch (errno) { DWORD error = GetLastError();
case EINVAL: switch (error) {
return HALLOCY_ERROR_INVALID_PARAM; case ERROR_INVALID_PARAMETER:
return HALLOCY_ERROR_INVALID_PARAM;
case ENOMEM: case ERROR_NOT_ENOUGH_MEMORY:
return HALLOCY_ERROR_OUT_OF_MEMORY; return HALLOCY_ERROR_OUT_OF_MEMORY;
case EFAULT: case ERROR_INVALID_HANDLE:
return HALLOCY_ERROR_INVALID_POINTER; return HALLOCY_ERROR_INVALID_POINTER;
default: case ERROR_INVALID_ADDRESS:
return HALLOCY_ERROR_UNKNOWN; return HALLOCY_ERROR_INVALID_POINTER;
default:
return HALLOCY_ERROR_UNKNOWN;
}
} }
#elif defined(__linux__)
int result = munmap(memory_header, memory_header->size);
if (result == -1) {
switch (errno) {
case EINVAL:
return HALLOCY_ERROR_INVALID_PARAM;
case ENOMEM:
return HALLOCY_ERROR_OUT_OF_MEMORY;
case EFAULT:
return HALLOCY_ERROR_INVALID_POINTER;
default:
return HALLOCY_ERROR_UNKNOWN;
}
}
#endif
} }
#endif
return HALLOCY_ERROR_NONE; return HALLOCY_ERROR_NONE;
} }