From bfbabc13bc2d8cb973b764b7f68940284a14aa94 Mon Sep 17 00:00:00 2001 From: Dmitriy Musatkin Date: Mon, 23 Oct 2023 09:47:30 -0700 Subject: [PATCH] no align allocator --- include/aws/common/allocator.h | 25 +++++++++++++++++ source/allocator.c | 51 ++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 2 ++ tests/alloc_test.c | 42 +++++++++++++++++++++++++--- 4 files changed, 116 insertions(+), 4 deletions(-) diff --git a/include/aws/common/allocator.h b/include/aws/common/allocator.h index c0e1c85af..d4880f846 100644 --- a/include/aws/common/allocator.h +++ b/include/aws/common/allocator.h @@ -12,6 +12,25 @@ AWS_PUSH_SANE_WARNING_LEVEL AWS_EXTERN_C_BEGIN +/* + * Quick guide to allocators: + * CRT offers several flavours of allocators: + * - default: basic allocator, targeted toward most common use case. behavior + * can change over time. Current default aligns small allocations on 8 byte + * boundary and big buffers on 32/64 byte (system dependent) boundary. + * Aligned mem can improve perf on some operations, like memcpy or hashes. + * Depending on a system, can result in higher peak memory count in heavy + * acquire/free scenarios (ex. s3), due to memory fragmentation related to how + * aligned allocators work (overallocate, find aligned offset, release extra memory) + * - no_align: similar to default, but does not do any alignment. In practice, + * has smaller peak memory footprint than default allocator, at the cost of + * potentially being slower. + * - wrapped_cf: wraps MacOS's Security Framework allocator. + * - mem_tracer: wraps any allocator and provides tracing functionality to allocations + * - small_block_allocator: pools smaller allocations into preallocated buckets. + * Not actively maintained. Avoid if possible. + */ + /* Allocator structure. An instance of this will be passed around for anything needing memory allocation */ struct aws_allocator { void *(*mem_acquire)(struct aws_allocator *allocator, size_t size); @@ -32,6 +51,12 @@ bool aws_allocator_is_valid(const struct aws_allocator *alloc); AWS_COMMON_API struct aws_allocator *aws_default_allocator(void); +/* + * Vanilla allocator. No extra logic on top of system allocator. + */ +AWS_COMMON_API +struct aws_allocator *aws_no_align_allocator(void); + #ifdef __MACH__ /* Avoid pulling in CoreFoundation headers in a header file. */ struct __CFAllocator; /* NOLINT(bugprone-reserved-identifier) */ diff --git a/source/allocator.c b/source/allocator.c index e444d282a..c07c58336 100644 --- a/source/allocator.c +++ b/source/allocator.c @@ -117,6 +117,57 @@ struct aws_allocator *aws_default_allocator(void) { return &default_allocator; } +static void *s_no_align_malloc(struct aws_allocator *allocator, size_t size) { + (void)allocator; + void *result = malloc(size); + AWS_PANIC_OOM(result, "posix_memalign failed to allocate memory"); + return result; +} + +static void s_no_align_free(struct aws_allocator *allocator, void *ptr) { + (void)allocator; + free(ptr); +} + +static void *s_no_align_realloc(struct aws_allocator *allocator, void *ptr, size_t oldsize, size_t newsize) { + (void)allocator; + (void)oldsize; + AWS_FATAL_PRECONDITION(newsize); + + if (newsize <= oldsize) { + return ptr; + } + + /* newsize is > oldsize, need more memory */ + void *new_mem = s_no_align_malloc(allocator, newsize); + AWS_PANIC_OOM(new_mem, "Unhandled OOM encountered in s_default_malloc"); + + if (ptr) { + memcpy(new_mem, ptr, oldsize); + s_no_align_free(allocator, ptr); + } + + return new_mem; +} + +static void *s_no_align_calloc(struct aws_allocator *allocator, size_t num, size_t size) { + (void)allocator; + void *mem = calloc(num, size); + AWS_PANIC_OOM(mem, "Unhandled OOM encountered in s_default_malloc"); + return mem; +} + +static struct aws_allocator no_align_allocator = { + .mem_acquire = s_no_align_malloc, + .mem_release = s_no_align_free, + .mem_realloc = s_no_align_realloc, + .mem_calloc = s_no_align_calloc, +}; + +struct aws_allocator *aws_no_align_allocator(void) { + return &no_align_allocator; +} + void *aws_mem_acquire(struct aws_allocator *allocator, size_t size) { AWS_FATAL_PRECONDITION(allocator != NULL); AWS_FATAL_PRECONDITION(allocator->mem_acquire != NULL); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4681e04c3..a35328083 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -320,6 +320,8 @@ add_test_case(sba_churn) add_test_case(sba_metrics) add_test_case(default_threaded_reallocs) add_test_case(default_threaded_allocs_and_frees) +add_test_case(no_align_threaded_reallocs) +add_test_case(no_align_threaded_allocs_and_frees) add_test_case(test_memtrace_none) add_test_case(test_memtrace_count) diff --git a/tests/alloc_test.c b/tests/alloc_test.c index 66ff95999..db11afa9a 100644 --- a/tests/alloc_test.c +++ b/tests/alloc_test.c @@ -48,10 +48,11 @@ static int s_test_alloc_nothing_fn(struct aws_allocator *allocator, void *ctx) { (void)allocator; (void)ctx; - struct aws_allocator test_allocator = {.mem_acquire = s_test_alloc_acquire, - .mem_release = s_test_alloc_release, - .mem_realloc = s_test_realloc, - .mem_calloc = s_test_calloc}; + struct aws_allocator test_allocator = { + .mem_acquire = s_test_alloc_acquire, + .mem_release = s_test_alloc_release, + .mem_realloc = s_test_realloc, + .mem_calloc = s_test_calloc}; /* realloc should handle the case correctly, return null, and free the memory */ void *p = aws_mem_acquire(&test_allocator, 12); @@ -346,3 +347,36 @@ static int s_default_threaded_allocs_and_frees(struct aws_allocator *allocator, return 0; } AWS_TEST_CASE(default_threaded_allocs_and_frees, s_default_threaded_allocs_and_frees) + +/* + * No align allocator tests. + */ +static int s_no_align_threaded_reallocs(struct aws_allocator *allocator, void *ctx) { + (void)allocator; + (void)ctx; + srand(15); + + struct aws_allocator *alloc = aws_mem_tracer_new(aws_no_align_allocator(), NULL, AWS_MEMTRACE_STACKS, 8); + + s_thread_test(alloc, s_threaded_realloc_worker, alloc); + + aws_mem_tracer_destroy(alloc); + + return 0; +} +AWS_TEST_CASE(no_align_threaded_reallocs, s_no_align_threaded_reallocs) + +static int s_no_align_threaded_allocs_and_frees(struct aws_allocator *allocator, void *ctx) { + (void)allocator; + (void)ctx; + srand(99); + + struct aws_allocator *alloc = aws_mem_tracer_new(aws_no_align_allocator(), NULL, AWS_MEMTRACE_STACKS, 8); + + s_thread_test(alloc, s_threaded_alloc_worker, alloc); + + aws_mem_tracer_destroy(alloc); + + return 0; +} +AWS_TEST_CASE(no_align_threaded_allocs_and_frees, s_no_align_threaded_allocs_and_frees)