diff --git a/allocator/testallocator/alloc.c b/allocator/testallocator/alloc.c new file mode 100644 index 0000000..cf9dae3 --- /dev/null +++ b/allocator/testallocator/alloc.c @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MAXPAGESIZES 2 + +static char *heap_start; +static char *heap; +static size_t HEAP_SIZE = 1024 * 1024 * 1024; + +void *ptr; +int MallocCounter; +int malloc_called = 0; + +size_t sizeUsed; + +// Instrcutor allocator to create the huge page +// of 1 GB +// __attribute__((constructor)) +static void INITREGULARALLOC() { + size_t sz; + // Hard-coded for 1GB huge page + sz = 1073741824; + + int error, fd, pscnt, pn; + + size_t ps[2]; + + size_t size[3]; + + // pn = getpagesizes(size, 3); + // printf("page size is [%d]", size[2]); + + // pscnt = pagesizes(ps); + + fd = shm_create_largepage(SHM_ANON, O_CREAT | O_RDWR, 1, SHM_LARGEPAGE_ALLOC_DEFAULT, 0); + + if (fd < 0 && errno == ENOTTY) { + perror("sh_create_largepages"); + close(fd); + exit(EXIT_FAILURE); + } + // if (fd < 0) + // perror("no large page supported"); + // exit(EXIT_FAILURE); + // if (fd < 0 && errno == ENOTTY) + // atf_tc_skip("no large page support"); + // ATF_REQUIRE_MSG(fd >= 0, "shm_create_largepage failed; errno=%d", errno); + + if (ftruncate(fd, sz) < 0) { + perror("ftruncate"); + close(fd); + exit(EXIT_FAILURE); + } + // if (error != 0 && errno == ENOMEM) + // /* + // * The test system might not have enough memory to accommodate + // * the request. + // */ + // atf_tc_skip("failed to allocate %zu-byte superpage", sz); + // ATF_REQUIRE_MSG(error == 0, "ftruncate failed; errno=%d", errno); + + ptr = mmap(NULL, sz, + PROT_READ|PROT_WRITE, MAP_SHARED,fd,0); + + // Added error handling + if(ptr == MAP_FAILED) + { + perror("mmap"); + exit(EXIT_FAILURE); + } + + + // fprintf(stderr, "heap used alloc %lu\n", heap - heap_start); + + MallocCounter = (int)sz; +} + +// -- Custom malloc and free functions written +// This will be replaced with mmap since we already +// do a initial mmap. + +int notrun = 0; + +void *MALLOCCHERI(size_t sz) +{ + if (notrun == 0){ + INITREGULARALLOC(); + notrun = 1; + } + + sz = __builtin_align_up(sz, _Alignof(max_align_t)); + + // printf("%d \n", sz); + // printf("%d Malloc counter\n", MallocCounter); + + MallocCounter -= sz; + void *ptrLink = &ptr[MallocCounter]; + ptrLink = cheri_setbounds(ptrLink, sz); + + return ptrLink; + +// if (heap + sz > heap_start + HEAP_SIZE) return NULL; +// heap += sz; +// return heap - sz; + +} + +// Quick cheri free implementation +void FREECHERI(void *ptr) { + + // printf("free called \n"); + + // get bounds from + int len = cheri_getlen(ptr); + + // printf("free len %d \n", len); + + munmap(ptr, len); +} + +__attribute__((destructor)) +static void malloc_exit() { + fprintf(stderr, "heap used %lu\n", malloc_called); +} + +// void *malloc(size_t sz) { +// // if (!heap) heap = heap_start = mmap(NULL, HEAP_SIZE, +// // PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,-1,0); + +// // char *new_ptr = __builtin_align_up( +// // heap, -cheri_representable_alignment_mask(sz)); +// // size_t bounds = cheri_representable_length(sz); +// // sz = __builtin_align_up(sz, _Alignof(max_align_t)); + +// // if (new_ptr + sz > heap_start + HEAP_SIZE) +// // return NULL; +// // heap = new_ptr + sz; +// // return cheri_bounds_set_exact(new_ptr, bounds); +// return MALLOCCHERI(sz); +// } + +void *malloc(size_t sz) { + // if (!heap) heap = heap_start = mmap(NULL, HEAP_SIZE, + // PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,-1,0); + // sz = __builtin_align_up(sz, _Alignof(max_align_t)); + // if (heap + sz > heap_start + HEAP_SIZE) return NULL; + // heap += sz; + // return heap - sz; + malloc_called += 1; + return MALLOCCHERI(sz); +} + +void free(void *ptr) { + FREECHERI(ptr); +} + +void *realloc(void *ptr, size_t sz) { + void *new_ptr = malloc(sz); + if (ptr && new_ptr) memmove(new_ptr, ptr, sz); + return new_ptr; +} + +void *calloc(size_t nmemb, size_t sz) { + char *ptr = malloc(nmemb * sz); + bzero(ptr, nmemb * sz); + return ptr; +} + +void *reallocarray(void *ptr, size_t nmemb, size_t sz) { + return realloc(ptr, nmemb * sz); +} + +void *recallocarray(void *ptr, size_t oldnmemb, size_t nmemb, size_t sz) { + void *new_ptr = malloc(nmemb * sz); + if (ptr && new_ptr) memmove(new_ptr, ptr, oldnmemb * sz); + if (new_ptr && nmemb > oldnmemb) bzero(new_ptr + oldnmemb * sz, (nmemb - oldnmemb) * sz); + return new_ptr; +} + +void freezero(void *ptr, size_t sz) { } + +void *aligned_alloc(size_t alignment, size_t sz) { + return malloc(sz); +} + +void *malloc_conceal(size_t sz) { + return malloc(sz); +} + +void *calloc_conceal(size_t nmemb, size_t sz) { + return calloc(nmemb, sz); +} + +int posix_memalign(void **ptr, size_t alignment, size_t sz) { + *ptr = malloc(sz); + return *ptr == 0 ? 0 : ENOMEM; +} + +void *memalign(size_t alignment, size_t sz) { + return malloc(sz); +} + +void *valloc(size_t sz) { + return malloc(sz); +} diff --git a/allocator/testallocator/build.sh b/allocator/testallocator/build.sh new file mode 100644 index 0000000..8ec84b9 --- /dev/null +++ b/allocator/testallocator/build.sh @@ -0,0 +1,7 @@ +# build glibc +cc -g -Wall -o glibc-bench.out -march=morello -mabi=purecap -Xclang -morello-vararg=new -lpthread glibc.c + +# build shared object library +cc -O3 -g -W -Wall -shared -o ./malloc.so -mabi=purecap -Wno-unused-parameter -lpthread -fPIC alloc.c + +LD_PRELOAD=malloc.so ./glibc-bench.out diff --git a/allocator/testallocator/glibc.c b/allocator/testallocator/glibc.c new file mode 100644 index 0000000..8a73b4f --- /dev/null +++ b/allocator/testallocator/glibc.c @@ -0,0 +1,217 @@ +/* Benchmark malloc and free functions. + Copyright (C) 2019-2021 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +// modified by Daan Leijen to fit the bench suite and add lifo/fifo free order. + +#include +#include +#include +#include +//#include "malloc.h" + +// #define malloc MALLOCCHERI +// #define free FREECHERI + +// #include "bench-timing.h" +// #include "json-lib.h" + +/* Benchmark the malloc/free performance of a varying number of blocks of a + given size. This enables performance tracking of the t-cache and fastbins. + It tests 3 different scenarios: single-threaded using main arena, + multi-threaded using thread-arena, and main arena with SINGLE_THREAD_P + false. */ + +// source: https://github.com/daanx/mimalloc-bench/blob/master/bench/glibc-bench/bench-malloc-thread.c + +#define NUM_ITERS 2000000 +#define NUM_ALLOCS 4 +#define MAX_ALLOCS 1600 + +// Daan: disable timing +typedef long timing_t; +#define TIMING_NOW(s) +#define TIMING_DIFF(e,start,stop) + + +typedef struct +{ + size_t iters; + size_t size; + int n; + timing_t elapsed; +} malloc_args; + +static void +do_benchmark (malloc_args *args, char**arr) +{ + timing_t start, stop; + size_t iters = args->iters; + size_t size = args->size; + int n = args->n; + + TIMING_NOW (start); + + for (int j = 0; j < iters; j++) + { + for (int i = 0; i < n; i++) { + arr[i] = malloc (size); + for(int g = 0; g < size; g++) { arr[i][g] =(char)g; } + } + + // free half in fifo order + for (int i = 0; i < n/2; i++) { + free (arr[i]); + } + + // and the other half in lifo order + for(int i = n-1; i >= n/2; i--) { + free(arr[i]); + } + } + + TIMING_NOW (stop); + + TIMING_DIFF (args->elapsed, start, stop); +} + +static malloc_args tests[3][NUM_ALLOCS]; +static int allocs[NUM_ALLOCS] = { 25, 100, 400, MAX_ALLOCS }; + +static void * +thread_test (void *p) +{ + char **arr = (char**)p; + + /* Run benchmark multi-threaded. */ + for (int i = 0; i < NUM_ALLOCS; i++) + do_benchmark (&tests[2][i], arr); + + return p; +} + +void +bench (unsigned long size) +{ + size_t iters = NUM_ITERS; + char**arr = (char**)malloc (MAX_ALLOCS * sizeof (void*)); + + for (int t = 0; t < 3; t++) + for (int i = 0; i < NUM_ALLOCS; i++) + { + tests[t][i].n = allocs[i]; + tests[t][i].size = size; + tests[t][i].iters = iters / allocs[i]; + + /* Do a quick warmup run. */ + if (t == 0) + do_benchmark (&tests[0][i], arr); + } + + /* Run benchmark single threaded in main_arena. */ + for (int i = 0; i < NUM_ALLOCS; i++) + do_benchmark (&tests[0][i], arr); + + /* Run benchmark in a thread_arena. */ + pthread_t t; + pthread_create (&t, NULL, thread_test, (void*)arr); + pthread_join (t, NULL); + + /* Repeat benchmark in main_arena with SINGLE_THREAD_P == false. */ + for (int i = 0; i < NUM_ALLOCS; i++) + do_benchmark (&tests[1][i], arr); + + free (arr); + + /* + json_ctx_t json_ctx; + + json_init (&json_ctx, 0, stdout); + + json_document_begin (&json_ctx); + + json_attr_string (&json_ctx, "timing_type", TIMING_TYPE); + + json_attr_object_begin (&json_ctx, "functions"); + + json_attr_object_begin (&json_ctx, "malloc"); + + char s[100]; + double iters2 = iters; + + json_attr_object_begin (&json_ctx, ""); + json_attr_double (&json_ctx, "malloc_block_size", size); + + struct rusage usage; + getrusage (RUSAGE_SELF, &usage); + json_attr_double (&json_ctx, "max_rss", usage.ru_maxrss); + + for (int i = 0; i < NUM_ALLOCS; i++) + { + sprintf (s, "main_arena_st_allocs_%04d_time", allocs[i]); + json_attr_double (&json_ctx, s, tests[0][i].elapsed / iters2); + } + + for (int i = 0; i < NUM_ALLOCS; i++) + { + sprintf (s, "main_arena_mt_allocs_%04d_time", allocs[i]); + json_attr_double (&json_ctx, s, tests[1][i].elapsed / iters2); + } + + for (int i = 0; i < NUM_ALLOCS; i++) + { + sprintf (s, "thread_arena__allocs_%04d_time", allocs[i]); + json_attr_double (&json_ctx, s, tests[2][i].elapsed / iters2); + } + + json_attr_object_end (&json_ctx); + + json_attr_object_end (&json_ctx); + + json_attr_object_end (&json_ctx); + + json_document_end (&json_ctx); + */ +} + +static void usage (const char *name) +{ + fprintf (stderr, "%s: \n", name); + exit (1); +} + +int +main (int argc, char **argv) +{ + //INITREGULARALLOC(); + long size = 16; + if (argc == 2) + size = strtol (argv[1], NULL, 0); + + if (argc > 2 || size <= 0) + usage (argv[0]); + + // bench (size); + bench (2*size); + printf("done"); + //bench (4*size); + //bench (8*size); + // bench (16*size); + // bench (32*size); + + return 0; +} diff --git a/benchmarks/benchmarks/kmeans/simple.h b/benchmarks/benchmarks/kmeans/simple.h index 35c3a98..1cf8904 100644 --- a/benchmarks/benchmarks/kmeans/simple.h +++ b/benchmarks/benchmarks/kmeans/simple.h @@ -49,7 +49,7 @@ // perror("mmap"); // return; // } -// #else +// #else // mem = calloc(pool, 1); // if (!mem) { // fprintf(stderr, "alloc_init(): could not allocate heap\n"); @@ -58,7 +58,7 @@ // #endif // // Each bitmap entry can represent 8 blocks and each block is 16 bytes // // So space representable in one uint8_t is 16 * 8 = 128 bytes -// uint64_t sz = pool / 128; +// uint64_t sz = pool / 128; // if (sz == 0) // sz = 1; // allocate at least one to keep track of small pools @@ -89,19 +89,19 @@ // int rem = sz % 16; // sz += (16 - rem); // } -// // Allocate two extra blocks +// // Allocate two extra blocks // // First will be allocated at just behind the first user accessible block // // This block will have the number of blocks allocated and a randomly generated magic number each 8 bytes long // // The last block has the "magic number" present in the first block -// // If this magic number gets modified then when free() tries to free the memory +// // If this magic number gets modified then when free() tries to free the memory // // Buffer overruns will be caught and this allocator gets poisoned i.e it can no longer allocate memory -// // This is because all blocks are laid out sequentially and if the user overruns the blocks allocated +// // This is because all blocks are laid out sequentially and if the user overruns the blocks allocated // // Then the user may have overwritten the contents of other blocks and it is not possible to estimate the damage caused // // and data corrupted. All pointers to blocks allocated immediately become invalid and free() posions the allocator -// // This helps catch buffer overflows early on +// // This helps catch buffer overflows early on // uint64_t blk = (sz / 16) + 2; -// // if we are posioned, all allocation requests will fail +// // if we are posioned, all allocation requests will fail // if (poison) // return 0; // // Loop through the entire bitmap. If a free block is found, check if there are at least blk free blocks after it. @@ -134,7 +134,7 @@ // // Return the user a pointer which points to the region just above our metadata block // return mem + ((i + 1) * 16); // } -// next: +// next: // } // fprintf(stderr, "Pool has been exhausted...Cannot allocate more memory"); // return 0; @@ -165,7 +165,7 @@ // offset -= 16; // offset /= 16; -// // Clear all bits representing this block so next call to alloc() can use this +// // Clear all bits representing this block so next call to alloc() can use this // for (uint64_t j = offset; j < offset + blk + 1; j++) { // MARK(j); // } @@ -174,4 +174,4 @@ // // Do not call this unless you are absolutely sure about the cause of poisoning // void clear_posion() { // poison = 0; -// } \ No newline at end of file +// } diff --git a/docs/EuroSys/Paper/paper.tex b/docs/EuroSys/Paper/paper.tex index 660774a..2fed952 100644 --- a/docs/EuroSys/Paper/paper.tex +++ b/docs/EuroSys/Paper/paper.tex @@ -843,6 +843,12 @@ for use with huge pages, demonstrates a clear advantage in scenarios where memor patterns benefit from its design. The results align with expectations, showcasing the impact of its capability to handle memory more efficiently by leveraging huge pages. + + - Each benchmark reflection for the metric + - summary based on each benchmark reflected + +- summarize all the bullet points. + A particularly striking observation is the significant reduction in data TLB walks, L2 data TLB reads, and TLB refills—consistently showing a 90\% decrease across all benchmarks compared to Jemalloc. This improvement is due to the modified allocator's