Skip to content

Add linear base allocator #177

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ set(UMF_LIBS umf_utils)

set(UMF_SOURCES
base_alloc/base_alloc.c
base_alloc/base_alloc_linear.c
memory_pool.c
memory_provider.c
memory_provider_get_last_failed.c
Expand Down
9 changes: 7 additions & 2 deletions src/base_alloc/base_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ umf_ba_pool_t *umf_ba_create(size_t size) {
char *data_ptr = (char *)&pool->data;
size_t size_left = pool_size - offsetof(umf_ba_pool_t, data);

align_ptr_size((void **)&data_ptr, &size_left, MEMORY_ALIGNMENT);

// allocate and init free_lock
pool->metadata.free_lock = util_mutex_init(data_ptr);
if (!pool->metadata.free_lock) {
Expand Down Expand Up @@ -178,9 +180,12 @@ void *umf_ba_alloc(umf_ba_pool_t *pool) {
pool->metadata.n_pools++;
#endif /* NDEBUG */

size_t size =
char *data_ptr = (char *)&new_pool->data;
size_t size_left =
pool->metadata.pool_size - offsetof(umf_ba_next_pool_t, data);
ba_divide_memory_into_chunks(pool, &new_pool->data, size);

align_ptr_size((void **)&data_ptr, &size_left, MEMORY_ALIGNMENT);
ba_divide_memory_into_chunks(pool, data_ptr, size_left);
}

umf_ba_chunk_t *chunk = pool->metadata.free_list;
Expand Down
90 changes: 90 additions & 0 deletions src/base_alloc/base_alloc_linear.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (C) 2024 Intel Corporation
*
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/

#include <assert.h>
#include <stdint.h>

#include "base_alloc_internal.h"
#include "base_alloc_linear.h"
#include "utils_common.h"
#include "utils_concurrency.h"

// minimum size of a single pool of the linear base allocator
#define MINIMUM_LINEAR_POOL_SIZE (ba_os_get_page_size())

// alignment of the linear base allocator
#define MEMORY_ALIGNMENT (sizeof(uintptr_t))

// metadata of the linear base allocator
typedef struct {
size_t pool_size;
os_mutex_t lock;
char *data_ptr;
size_t size_left;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you probably don't need size left - you can calculate that based on pool size and data_ptr. not sure whether that's worth changing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to have it saved, than calculate it each time - it is more clear for me.

} umf_ba_main_linear_pool_meta_t;

// pool of the linear base allocator
struct umf_ba_linear_pool {
umf_ba_main_linear_pool_meta_t metadata;
char data[]; // data area starts here
};

umf_ba_linear_pool_t *umf_ba_linear_create(size_t pool_size) {
size_t mutex_size = align_size(util_mutex_get_size(), MEMORY_ALIGNMENT);
size_t metadata_size = sizeof(umf_ba_main_linear_pool_meta_t);
pool_size = pool_size + metadata_size + mutex_size;
if (pool_size < MINIMUM_LINEAR_POOL_SIZE) {
pool_size = MINIMUM_LINEAR_POOL_SIZE;
}

pool_size = align_size(pool_size, ba_os_get_page_size());

umf_ba_linear_pool_t *pool = (umf_ba_linear_pool_t *)ba_os_alloc(pool_size);
if (!pool) {
return NULL;
}

void *data_ptr = &pool->data;
size_t size_left = pool_size - offsetof(umf_ba_linear_pool_t, data);

align_ptr_size(&data_ptr, &size_left, MEMORY_ALIGNMENT);

pool->metadata.pool_size = pool_size;
pool->metadata.data_ptr = data_ptr;
pool->metadata.size_left = size_left;

// init lock
os_mutex_t *lock = util_mutex_init(&pool->metadata.lock);
if (!lock) {
ba_os_free(pool, pool_size);
return NULL;
}

return pool;
}

void *umf_ba_linear_alloc(umf_ba_linear_pool_t *pool, size_t size) {
size_t aligned_size = align_size(size, MEMORY_ALIGNMENT);

util_mutex_lock(&pool->metadata.lock);
if (pool->metadata.size_left < aligned_size) {
util_mutex_unlock(&pool->metadata.lock);
return NULL; // out of memory
}

void *ptr = pool->metadata.data_ptr;
pool->metadata.data_ptr += aligned_size;
pool->metadata.size_left -= aligned_size;
util_mutex_unlock(&pool->metadata.lock);

return ptr;
}

void umf_ba_linear_destroy(umf_ba_linear_pool_t *pool) {
util_mutex_destroy_not_free(&pool->metadata.lock);
ba_os_free(pool, pool->metadata.pool_size);
}
26 changes: 26 additions & 0 deletions src/base_alloc/base_alloc_linear.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2024 Intel Corporation
*
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/

/*
* A MT-safe linear base allocator.
* Useful for a few, small and different size allocations
* for a most/whole life-time of an application
* (since free() is not available).
*/

#ifndef UMF_BASE_ALLOC_LINEAR_H
#define UMF_BASE_ALLOC_LINEAR_H 1

#include <stddef.h>

typedef struct umf_ba_linear_pool umf_ba_linear_pool_t;

umf_ba_linear_pool_t *umf_ba_linear_create(size_t pool_size);
void *umf_ba_linear_alloc(umf_ba_linear_pool_t *pool, size_t size);
void umf_ba_linear_destroy(umf_ba_linear_pool_t *pool);

#endif /* UMF_BASE_ALLOC_LINEAR_H */
20 changes: 20 additions & 0 deletions src/utils/utils_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#ifndef UMF_COMMON_H
#define UMF_COMMON_H 1

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -77,6 +78,25 @@ static inline void *Zalloc(size_t s) {
} \
} while (0)

// align a pointer and a size
static inline void align_ptr_size(void **ptr, size_t *size, size_t alignment) {
uintptr_t p = (uintptr_t)*ptr;
size_t s = *size;

// align pointer to 'alignment' bytes and adjust the size
size_t rest = p & (alignment - 1);
if (rest) {
p += alignment - rest;
s -= alignment - rest;
}

ASSERT((p & (alignment - 1)) == 0);
ASSERT((s & (alignment - 1)) == 0);

*ptr = (void *)p;
*size = s;
}

static inline size_t align_size(size_t size, size_t alignment) {
// align size to 'alignment' bytes
size_t rest = size & (alignment - 1);
Expand Down
11 changes: 8 additions & 3 deletions src/utils/utils_concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,21 @@
#if defined(_WIN32)
#include <windows.h>
#else
#include <pthread.h>
#include <stdatomic.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

struct os_mutex_t;

typedef struct os_mutex_t os_mutex_t;
typedef struct os_mutex_t {
#ifdef _WIN32
CRITICAL_SECTION lock;
#else
pthread_mutex_t lock;
#endif
} os_mutex_t;

size_t util_mutex_get_size(void);
os_mutex_t *util_mutex_init(void *ptr);
Expand Down
14 changes: 5 additions & 9 deletions src/utils/utils_windows_concurrency.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,10 @@

#include "utils_concurrency.h"

typedef struct {
CRITICAL_SECTION lock;
} internal_os_mutex_t;

size_t util_mutex_get_size(void) { return sizeof(internal_os_mutex_t); }
size_t util_mutex_get_size(void) { return sizeof(os_mutex_t); }

os_mutex_t *util_mutex_init(void *ptr) {
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)ptr;
os_mutex_t *mutex_internal = (os_mutex_t *)ptr;
InitializeCriticalSection(&mutex_internal->lock);
return (os_mutex_t *)mutex_internal;
}
Expand All @@ -26,7 +22,7 @@ os_mutex_t *util_mutex_create(void) {
}

void util_mutex_destroy_not_free(os_mutex_t *mutex) {
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)mutex;
os_mutex_t *mutex_internal = (os_mutex_t *)mutex;
DeleteCriticalSection(&mutex_internal->lock);
}

Expand All @@ -35,7 +31,7 @@ void util_mutex_destroy(os_mutex_t *mutex) {
}

int util_mutex_lock(os_mutex_t *mutex) {
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)mutex;
os_mutex_t *mutex_internal = (os_mutex_t *)mutex;
EnterCriticalSection(&mutex_internal->lock);

if (mutex_internal->lock.RecursionCount > 1) {
Expand All @@ -47,7 +43,7 @@ int util_mutex_lock(os_mutex_t *mutex) {
}

int util_mutex_unlock(os_mutex_t *mutex) {
internal_os_mutex_t *mutex_internal = (internal_os_mutex_t *)mutex;
os_mutex_t *mutex_internal = (os_mutex_t *)mutex;
LeaveCriticalSection(&mutex_internal->lock);
return 0;
}
3 changes: 3 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,7 @@ if(LINUX)
add_umf_test(NAME base_alloc
SRCS test_base_alloc.c
LIBS umf_utils)
add_umf_test(NAME base_alloc_linear
SRCS test_base_alloc_linear.c
LIBS umf_utils)
endif()
77 changes: 77 additions & 0 deletions test/test_base_alloc_linear.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (C) 2024 Intel Corporation
*
* Under the Apache License v2.0 with LLVM Exceptions. See LICENSE.TXT.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/

#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>

#include "base_alloc_linear.h"
#include "test_helpers.h"

#define NTHREADS 10
#define ITERATIONS 1000
#define MAX_ALLOCATION_SIZE 1024

static void *start_routine(void *arg) {
struct buffer_t {
unsigned char *ptr;
size_t size;
} buffer[ITERATIONS];
umf_ba_linear_pool_t *pool = (umf_ba_linear_pool_t *)arg;

long TID = syscall(SYS_gettid);

for (int i = 0; i < ITERATIONS; i++) {
buffer[i].size = (rand() * MAX_ALLOCATION_SIZE) / RAND_MAX;
buffer[i].ptr = umf_ba_linear_alloc(pool, buffer[i].size);
memset(buffer[i].ptr, (i + TID) & 0xFF, buffer[i].size);
}

for (int i = 0; i < ITERATIONS; i++) {
for (int k = 0; k < buffer[i].size; k++) {
if (*(buffer[i].ptr + k) != ((i + TID) & 0xFF)) {
fprintf(
stderr,
"i = %i k = %i, *(buffer[i].ptr + k) = %i != ((i + TID) & "
"0xFF) = %li\n",
i, k, *(buffer[i].ptr + k), ((i + TID) & 0xFF));
}
UT_ASSERTeq(*(buffer[i].ptr + k), ((i + TID) & 0xFF));
}
}

return NULL;
}

int main() {
pthread_t thread[NTHREADS];
umf_ba_linear_pool_t *pool = umf_ba_linear_create(MAX_ALLOCATION_SIZE);

for (int i = 0; i < NTHREADS; i++) {
int ret = pthread_create(&thread[i], NULL, start_routine, pool);
if (ret) {
fprintf(stderr, "pthread_create() failed!\n");
UT_ASSERTeq(ret, 0);
}
}

for (int i = 0; i < NTHREADS; i++) {
void *retval;
int ret = pthread_join(thread[i], &retval);
if (ret) {
fprintf(stderr, "pthread_join() failed!\n");
UT_ASSERTeq(ret, 0);
}
}

umf_ba_linear_destroy(pool);

return 0;
}