Skip to content

Commit cde20c6

Browse files
authored
aws_explicit_aligned_allocator_new (#1147)
1 parent 7b24a8a commit cde20c6

File tree

4 files changed

+128
-19
lines changed

4 files changed

+128
-19
lines changed

include/aws/common/allocator.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,19 @@ size_t aws_small_block_allocator_page_size(struct aws_allocator *sba_allocator);
228228
AWS_COMMON_API
229229
size_t aws_small_block_allocator_page_size_available(struct aws_allocator *sba_allocator);
230230

231+
/*
232+
* Create an aligned allocator with an explicit alignment. Always align the allocated buffer with the passed-in
233+
* alignment value.
234+
*/
235+
AWS_COMMON_API
236+
struct aws_allocator *aws_explicit_aligned_allocator_new(size_t alignment);
237+
238+
/*
239+
* Destroys a customized aligned allocator instance and frees its memory.
240+
*/
241+
AWS_COMMON_API
242+
void aws_explicit_aligned_allocator_destroy(struct aws_allocator *aligned_alloc);
243+
231244
AWS_EXTERN_C_END
232245
AWS_POP_SANE_WARNING_LEVEL
233246

source/allocator.c

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,28 @@ bool aws_allocator_is_valid(const struct aws_allocator *alloc) {
3535
}
3636

3737
static void *s_aligned_malloc(struct aws_allocator *allocator, size_t size) {
38-
(void)allocator;
39-
/* larger allocations should be aligned so that AVX and friends can avoid
40-
* the extra preamble during unaligned versions of memcpy/memset on big buffers
41-
* This will also accelerate hardware CRC and SHA on ARM chips
42-
*
43-
* 64 byte alignment for > page allocations on 64 bit systems
44-
* 32 byte alignment for > page allocations on 32 bit systems
45-
* 16 byte alignment for <= page allocations on 64 bit systems
46-
* 8 byte alignment for <= page allocations on 32 bit systems
47-
*
48-
* We use PAGE_SIZE as the boundary because we are not aware of any allocations of
49-
* this size or greater that are not data buffers
50-
*/
51-
const size_t alignment = sizeof(void *) * (size > (size_t)PAGE_SIZE ? 8 : 2);
38+
size_t alignment = 0;
39+
if (allocator->impl != NULL) {
40+
alignment = (size_t)allocator->impl;
41+
} else {
42+
/**
43+
* For implicit alignment.
44+
* larger allocations should be aligned so that AVX and friends can avoid
45+
* the extra preamble during unaligned versions of memcpy/memset on big buffers
46+
* This will also accelerate hardware CRC and SHA on ARM chips
47+
*
48+
* 64 byte alignment for > page allocations on 64 bit systems
49+
* 32 byte alignment for > page allocations on 32 bit systems
50+
* 16 byte alignment for <= page allocations on 64 bit systems
51+
* 8 byte alignment for <= page allocations on 32 bit systems
52+
*
53+
* We use PAGE_SIZE as the boundary because we are not aware of any allocations of
54+
* this size or greater that are not data buffers.
55+
*
56+
* Unless there is a customized alignment size.
57+
*/
58+
alignment = sizeof(void *) * (size > (size_t)PAGE_SIZE ? 8 : 2);
59+
}
5260
#if !defined(_WIN32)
5361
void *result = NULL;
5462
int err = posix_memalign(&result, alignment, size);
@@ -146,26 +154,48 @@ static void *s_non_aligned_calloc(struct aws_allocator *allocator, size_t num, s
146154
return mem;
147155
}
148156

149-
static struct aws_allocator default_allocator = {
157+
static struct aws_allocator s_default_allocator = {
150158
.mem_acquire = s_non_aligned_malloc,
151159
.mem_release = s_non_aligned_free,
152160
.mem_realloc = s_non_aligned_realloc,
153161
.mem_calloc = s_non_aligned_calloc,
154162
};
155163

156164
struct aws_allocator *aws_default_allocator(void) {
157-
return &default_allocator;
165+
return &s_default_allocator;
158166
}
159167

160-
static struct aws_allocator aligned_allocator = {
168+
static struct aws_allocator s_implicit_aligned_allocator = {
161169
.mem_acquire = s_aligned_malloc,
162170
.mem_release = s_aligned_free,
163171
.mem_realloc = s_aligned_realloc,
164172
.mem_calloc = s_aligned_calloc,
165173
};
166174

167175
struct aws_allocator *aws_aligned_allocator(void) {
168-
return &aligned_allocator;
176+
return &s_implicit_aligned_allocator;
177+
}
178+
179+
struct aws_allocator *aws_explicit_aligned_allocator_new(size_t customized_alignment) {
180+
if (customized_alignment == 0 || (customized_alignment & (customized_alignment - 1)) != 0 ||
181+
customized_alignment % sizeof(void *) != 0) {
182+
/**
183+
* the alignment must be a power of two and a multiple of sizeof(void *) and non-zero.
184+
*/
185+
aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
186+
return NULL;
187+
}
188+
struct aws_allocator *aligned_alloc = aws_mem_calloc(aws_default_allocator(), 1, sizeof(struct aws_allocator));
189+
*aligned_alloc = s_implicit_aligned_allocator;
190+
aligned_alloc->impl = (void *)customized_alignment;
191+
return aligned_alloc;
192+
}
193+
194+
void aws_explicit_aligned_allocator_destroy(struct aws_allocator *aligned_alloc) {
195+
if (!aligned_alloc) {
196+
return;
197+
}
198+
aws_mem_release(aws_default_allocator(), aligned_alloc);
169199
}
170200

171201
void *aws_mem_acquire(struct aws_allocator *allocator, size_t size) {

tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,9 @@ add_test_case(default_threaded_reallocs)
327327
add_test_case(default_threaded_allocs_and_frees)
328328
add_test_case(aligned_threaded_reallocs)
329329
add_test_case(aligned_threaded_allocs_and_frees)
330+
add_test_case(explicit_aligned_sanitize)
331+
add_test_case(explicit_aligned_threaded_reallocs)
332+
add_test_case(explicit_aligned_threaded_allocs_and_frees)
330333

331334
add_test_case(test_memtrace_none)
332335
add_test_case(test_memtrace_count)

tests/alloc_test.c

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ static int s_default_threaded_allocs_and_frees(struct aws_allocator *allocator,
350350
AWS_TEST_CASE(default_threaded_allocs_and_frees, s_default_threaded_allocs_and_frees)
351351

352352
/*
353-
* No align allocator tests.
353+
* aligned allocator tests.
354354
*/
355355
static int s_aligned_threaded_reallocs(struct aws_allocator *allocator, void *ctx) {
356356
(void)allocator;
@@ -381,3 +381,66 @@ static int s_aligned_threaded_allocs_and_frees(struct aws_allocator *allocator,
381381
return 0;
382382
}
383383
AWS_TEST_CASE(aligned_threaded_allocs_and_frees, s_aligned_threaded_allocs_and_frees)
384+
385+
static int s_explicit_aligned_sanitize(struct aws_allocator *allocator, void *ctx) {
386+
(void)allocator;
387+
(void)ctx;
388+
389+
struct aws_allocator *aligned_alloc = aws_explicit_aligned_allocator_new(1);
390+
ASSERT_NULL(aligned_alloc);
391+
ASSERT_UINT_EQUALS(aws_last_error(), AWS_ERROR_INVALID_ARGUMENT);
392+
393+
aligned_alloc = aws_explicit_aligned_allocator_new(3 * sizeof(void *));
394+
ASSERT_NULL(aligned_alloc);
395+
ASSERT_UINT_EQUALS(aws_last_error(), AWS_ERROR_INVALID_ARGUMENT);
396+
397+
aligned_alloc = aws_explicit_aligned_allocator_new(0);
398+
ASSERT_NULL(aligned_alloc);
399+
ASSERT_UINT_EQUALS(aws_last_error(), AWS_ERROR_INVALID_ARGUMENT);
400+
401+
size_t aligned_size = 1024;
402+
aligned_alloc = aws_explicit_aligned_allocator_new(aligned_size);
403+
ASSERT_NOT_NULL(aligned_alloc);
404+
void *test = aws_mem_acquire(aligned_alloc, sizeof(void *));
405+
ASSERT_TRUE((uintptr_t)test % aligned_size == 0);
406+
aws_mem_release(aligned_alloc, test);
407+
408+
aws_explicit_aligned_allocator_destroy(aligned_alloc);
409+
410+
return 0;
411+
}
412+
AWS_TEST_CASE(explicit_aligned_sanitize, s_explicit_aligned_sanitize)
413+
414+
static int s_explicit_aligned_threaded_reallocs(struct aws_allocator *allocator, void *ctx) {
415+
(void)allocator;
416+
(void)ctx;
417+
srand(15);
418+
struct aws_allocator *aligned_alloc = aws_explicit_aligned_allocator_new(512);
419+
420+
struct aws_allocator *alloc = aws_mem_tracer_new(aligned_alloc, NULL, AWS_MEMTRACE_STACKS, 8);
421+
422+
s_thread_test(alloc, s_threaded_realloc_worker, alloc);
423+
424+
aws_mem_tracer_destroy(alloc);
425+
aws_explicit_aligned_allocator_destroy(aligned_alloc);
426+
427+
return 0;
428+
}
429+
AWS_TEST_CASE(explicit_aligned_threaded_reallocs, s_explicit_aligned_threaded_reallocs)
430+
431+
static int s_explicit_aligned_threaded_allocs_and_frees(struct aws_allocator *allocator, void *ctx) {
432+
(void)allocator;
433+
(void)ctx;
434+
srand(99);
435+
struct aws_allocator *aligned_alloc = aws_explicit_aligned_allocator_new(512);
436+
437+
struct aws_allocator *alloc = aws_mem_tracer_new(aligned_alloc, NULL, AWS_MEMTRACE_STACKS, 8);
438+
439+
s_thread_test(alloc, s_threaded_alloc_worker, alloc);
440+
441+
aws_mem_tracer_destroy(alloc);
442+
aws_explicit_aligned_allocator_destroy(aligned_alloc);
443+
444+
return 0;
445+
}
446+
AWS_TEST_CASE(explicit_aligned_threaded_allocs_and_frees, s_explicit_aligned_threaded_allocs_and_frees)

0 commit comments

Comments
 (0)