Skip to content

Commit 5de8d28

Browse files
authored
Merge pull request #203 from igchor/ba_alloc_classes
Add abstraction on top of ba_alloc which supports arbitrary size allocations
2 parents fb6bded + 197ab24 commit 5de8d28

20 files changed

+215
-133
lines changed

src/base_alloc/base_alloc_global.c

Lines changed: 86 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,116 @@
55
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
*/
77

8+
/* A MT-safe base allocator */
9+
810
#include <assert.h>
11+
#include <stdio.h>
912
#include <stdlib.h>
1013

1114
#include "base_alloc.h"
1215
#include "base_alloc_global.h"
16+
#include "base_alloc_internal.h"
1317
#include "utils_concurrency.h"
14-
15-
#define SIZE_BA_POOL_CHUNK 128
18+
#include "utils_math.h"
1619

1720
// global base allocator used by all providers and pools
18-
static umf_ba_pool_t *BA_pool = NULL;
1921
static UTIL_ONCE_FLAG ba_is_initialized = UTIL_ONCE_FLAG_INIT;
2022

23+
// allocation classes need to be powers of 2
24+
#define ALLOCATION_CLASSES \
25+
{ 16, 32, 64, 128 }
26+
#define NUM_ALLOCATION_CLASSES 4
27+
28+
struct base_alloc_t {
29+
size_t ac_sizes[NUM_ALLOCATION_CLASSES];
30+
umf_ba_pool_t *ac[NUM_ALLOCATION_CLASSES];
31+
size_t smallest_ac_size_log2;
32+
};
33+
34+
static struct base_alloc_t BASE_ALLOC = {.ac_sizes = ALLOCATION_CLASSES};
35+
36+
void umf_ba_destroy_global(void) {
37+
for (int i = 0; i < NUM_ALLOCATION_CLASSES; i++) {
38+
if (BASE_ALLOC.ac[i]) {
39+
umf_ba_destroy(BASE_ALLOC.ac[i]);
40+
BASE_ALLOC.ac[i] = NULL;
41+
}
42+
}
43+
}
44+
2145
static void umf_ba_create_global(void) {
22-
assert(BA_pool == NULL);
23-
BA_pool = umf_ba_create(SIZE_BA_POOL_CHUNK);
24-
assert(BA_pool);
46+
for (int i = 0; i < NUM_ALLOCATION_CLASSES; i++) {
47+
// allocation classes need to be powers of 2
48+
assert(0 == (BASE_ALLOC.ac_sizes[i] & (BASE_ALLOC.ac_sizes[i] - 1)));
49+
BASE_ALLOC.ac[i] = umf_ba_create(BASE_ALLOC.ac_sizes[i]);
50+
if (!BASE_ALLOC.ac[i]) {
51+
fprintf(stderr,
52+
"base_alloc: Error. Cannot create base alloc allocation "
53+
"class for size: %zu\n. Each allocation will fallback to "
54+
"allocating memory from the OS.",
55+
BASE_ALLOC.ac_sizes[i]);
56+
}
57+
}
58+
59+
size_t smallestSize = BASE_ALLOC.ac_sizes[0];
60+
BASE_ALLOC.smallest_ac_size_log2 = log2Utils(smallestSize);
61+
2562
#if defined(_WIN32) && !defined(UMF_SHARED_LIBRARY)
2663
atexit(umf_ba_destroy_global);
2764
#endif
2865
}
2966

30-
void umf_ba_destroy_global(void) {
31-
if (BA_pool) {
32-
umf_ba_pool_t *pool = BA_pool;
33-
BA_pool = NULL;
34-
umf_ba_destroy(pool);
67+
// returns index of the allocation class for a given size
68+
static int size_to_idx(size_t size) {
69+
assert(size <= BASE_ALLOC.ac_sizes[NUM_ALLOCATION_CLASSES - 1]);
70+
71+
if (size <= BASE_ALLOC.ac_sizes[0]) {
72+
return 0;
3573
}
74+
75+
int isPowerOf2 = (0 == (size & (size - 1)));
76+
int index =
77+
(int)(log2Utils(size) + !isPowerOf2 - BASE_ALLOC.smallest_ac_size_log2);
78+
79+
assert(index >= 0);
80+
assert(index < NUM_ALLOCATION_CLASSES);
81+
82+
return index;
3683
}
3784

38-
umf_ba_pool_t *umf_ba_get_pool(size_t size) {
85+
void *umf_ba_global_alloc(size_t size) {
3986
util_init_once(&ba_is_initialized, umf_ba_create_global);
4087

41-
if (!BA_pool) {
42-
return NULL;
88+
if (size > BASE_ALLOC.ac_sizes[NUM_ALLOCATION_CLASSES - 1]) {
89+
fprintf(stderr,
90+
"base_alloc: allocation size larger than the biggest "
91+
"allocation class. Falling back to OS memory allocation.\n");
92+
return ba_os_alloc(size);
4393
}
4494

45-
// TODO: a specific class-size base allocator can be returned here
46-
assert(size <= SIZE_BA_POOL_CHUNK);
95+
int ac_index = size_to_idx(size);
96+
if (!BASE_ALLOC.ac[ac_index]) {
97+
// if creating ac failed, fall back to os allocation
98+
fprintf(stderr, "base_alloc: allocation class not created. Falling "
99+
"back to OS memory allocation.\n");
100+
return ba_os_alloc(size);
101+
}
102+
103+
return umf_ba_alloc(BASE_ALLOC.ac[ac_index]);
104+
}
105+
106+
void umf_ba_global_free(void *ptr, size_t size) {
107+
if (size > BASE_ALLOC.ac_sizes[NUM_ALLOCATION_CLASSES - 1]) {
108+
ba_os_free(ptr, size);
109+
return;
110+
}
47111

48-
if (size > SIZE_BA_POOL_CHUNK) {
49-
return NULL;
112+
int ac_index = size_to_idx(size);
113+
if (!BASE_ALLOC.ac[ac_index]) {
114+
// if creating ac failed, memory must have been allocated by os
115+
ba_os_free(ptr, size);
116+
return;
50117
}
51118

52-
return BA_pool;
119+
umf_ba_free(BASE_ALLOC.ac[ac_index], ptr);
53120
}

src/base_alloc/base_alloc_global.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
extern "C" {
1515
#endif
1616

17-
umf_ba_pool_t *umf_ba_get_pool(size_t size);
17+
void *umf_ba_global_alloc(size_t size);
18+
void umf_ba_global_free(void *ptr, size_t size);
1819
void umf_ba_destroy_global(void);
1920

2021
#ifdef __cplusplus

src/memory_pool_default.c

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,21 @@ umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
2525
}
2626

2727
umf_result_t ret = UMF_RESULT_SUCCESS;
28-
umf_ba_pool_t *base_allocator = umf_ba_get_pool(sizeof(umf_memory_pool_t));
29-
if (!base_allocator) {
30-
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
31-
}
32-
33-
umf_memory_pool_handle_t pool = umf_ba_alloc(base_allocator);
28+
umf_memory_pool_handle_t pool =
29+
umf_ba_global_alloc(sizeof(umf_memory_pool_t));
3430
if (!pool) {
3531
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
3632
}
3733

3834
assert(ops->version == UMF_VERSION_CURRENT);
3935

40-
pool->base_allocator = base_allocator;
4136
pool->provider = provider;
4237
pool->own_provider = false;
4338

4439
pool->ops = *ops;
4540
ret = ops->initialize(pool->provider, params, &pool->pool_priv);
4641
if (ret != UMF_RESULT_SUCCESS) {
47-
umf_ba_free(base_allocator, pool);
42+
umf_ba_global_free(pool, sizeof(umf_memory_pool_t));
4843
return ret;
4944
}
5045

@@ -61,7 +56,7 @@ void umfPoolDestroy(umf_memory_pool_handle_t hPool) {
6156
umfMemoryProviderDestroy(hProvider);
6257
}
6358
// TODO: this free keeps memory in base allocator, so it can lead to OOM in some scenarios (it should be optimized)
64-
umf_ba_free(hPool->base_allocator, hPool);
59+
umf_ba_global_free(hPool, sizeof(umf_memory_pool_t));
6560
}
6661

6762
umf_result_t umfFree(void *ptr) {

src/memory_pool_internal.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ typedef struct umf_memory_pool_t {
3131
umf_memory_provider_handle_t provider;
3232
// Tells whether memory provider is owned by the pool.
3333
bool own_provider;
34-
35-
// saved pointer to the global base allocator
36-
umf_ba_pool_t *base_allocator;
3734
} umf_memory_pool_t;
3835

3936
umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,

src/memory_pool_tracking.c

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,8 @@ umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
2626
}
2727

2828
umf_result_t ret = UMF_RESULT_SUCCESS;
29-
umf_ba_pool_t *base_allocator = umf_ba_get_pool(sizeof(umf_memory_pool_t));
30-
if (!base_allocator) {
31-
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
32-
}
33-
34-
umf_memory_pool_handle_t pool = umf_ba_alloc(base_allocator);
29+
umf_memory_pool_handle_t pool =
30+
umf_ba_global_alloc(sizeof(umf_memory_pool_t));
3531
if (!pool) {
3632
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
3733
}
@@ -44,7 +40,6 @@ umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
4440
goto err_provider_create;
4541
}
4642

47-
pool->base_allocator = base_allocator;
4843
pool->own_provider = false;
4944

5045
pool->ops = *ops;
@@ -59,7 +54,7 @@ umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
5954
err_pool_init:
6055
umfMemoryProviderDestroy(pool->provider);
6156
err_provider_create:
62-
umf_ba_free(base_allocator, pool);
57+
umf_ba_global_free(pool, sizeof(umf_memory_pool_t));
6358
return ret;
6459
}
6560

@@ -74,7 +69,7 @@ void umfPoolDestroy(umf_memory_pool_handle_t hPool) {
7469
// Destroy tracking provider.
7570
umfMemoryProviderDestroy(hPool->provider);
7671
// TODO: this free keeps memory in base allocator, so it can lead to OOM in some scenarios (it should be optimized)
77-
umf_ba_free(hPool->base_allocator, hPool);
72+
umf_ba_global_free(hPool, sizeof(umf_memory_pool_t));
7873
}
7974

8075
umf_result_t umfFree(void *ptr) {

src/memory_provider.c

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@
2222
typedef struct umf_memory_provider_t {
2323
umf_memory_provider_ops_t ops;
2424
void *provider_priv;
25-
26-
// saved pointer to the global base allocator
27-
umf_ba_pool_t *base_allocator;
2825
} umf_memory_provider_t;
2926

3027
umf_result_t umfMemoryProviderCreate(const umf_memory_provider_ops_t *ops,
@@ -35,26 +32,20 @@ umf_result_t umfMemoryProviderCreate(const umf_memory_provider_ops_t *ops,
3532
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
3633
}
3734

38-
umf_ba_pool_t *base_allocator =
39-
umf_ba_get_pool(sizeof(umf_memory_provider_t));
40-
if (!base_allocator) {
41-
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
42-
}
43-
44-
umf_memory_provider_handle_t provider = umf_ba_alloc(base_allocator);
35+
umf_memory_provider_handle_t provider =
36+
umf_ba_global_alloc(sizeof(umf_memory_provider_t));
4537
if (!provider) {
4638
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
4739
}
4840

4941
assert(ops->version == UMF_VERSION_CURRENT);
5042

5143
provider->ops = *ops;
52-
provider->base_allocator = base_allocator;
5344

5445
void *provider_priv;
5546
umf_result_t ret = ops->initialize(params, &provider_priv);
5647
if (ret != UMF_RESULT_SUCCESS) {
57-
umf_ba_free(base_allocator, provider);
48+
umf_ba_global_free(provider, sizeof(umf_memory_provider_t));
5849
return ret;
5950
}
6051

@@ -67,7 +58,7 @@ umf_result_t umfMemoryProviderCreate(const umf_memory_provider_ops_t *ops,
6758

6859
void umfMemoryProviderDestroy(umf_memory_provider_handle_t hProvider) {
6960
hProvider->ops.finalize(hProvider->provider_priv);
70-
umf_ba_free(hProvider->base_allocator, hProvider);
61+
umf_ba_global_free(hProvider, sizeof(umf_memory_provider_t));
7162
}
7263

7364
static void

src/memory_target.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,27 +24,20 @@ umf_result_t umfMemoryTargetCreate(const umf_memory_target_ops_t *ops,
2424
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
2525
}
2626

27-
umf_ba_pool_t *base_allocator =
28-
umf_ba_get_pool(sizeof(umf_memory_target_t));
29-
if (!base_allocator) {
30-
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
31-
}
32-
3327
umf_memory_target_handle_t target =
34-
(umf_memory_target_t *)umf_ba_alloc(base_allocator);
28+
(umf_memory_target_t *)umf_ba_global_alloc(sizeof(umf_memory_target_t));
3529
if (!target) {
3630
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
3731
}
3832

3933
assert(ops->version == UMF_VERSION_CURRENT);
4034

41-
target->base_allocator = base_allocator;
4235
target->ops = ops;
4336

4437
void *target_priv;
4538
umf_result_t ret = ops->initialize(params, &target_priv);
4639
if (ret != UMF_RESULT_SUCCESS) {
47-
umf_ba_free(base_allocator, target);
40+
umf_ba_global_free(target, sizeof(umf_memory_target_t));
4841
return ret;
4942
}
5043

@@ -58,5 +51,5 @@ umf_result_t umfMemoryTargetCreate(const umf_memory_target_ops_t *ops,
5851
void umfMemoryTargetDestroy(umf_memory_target_handle_t memoryTarget) {
5952
assert(memoryTarget);
6053
memoryTarget->ops->finalize(memoryTarget->priv);
61-
umf_ba_free(memoryTarget->base_allocator, memoryTarget);
54+
umf_ba_global_free(memoryTarget, sizeof(umf_memory_target_t));
6255
}

src/memory_target.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ typedef struct umf_memory_target_ops_t umf_memory_target_ops_t;
2424
typedef struct umf_memory_target_t {
2525
const umf_memory_target_ops_t *ops;
2626
void *priv;
27-
28-
// saved pointer to the global base allocator
29-
umf_ba_pool_t *base_allocator;
3027
} umf_memory_target_t;
3128

3229
typedef umf_memory_target_t *umf_memory_target_handle_t;

src/memory_targets/memory_target_numa.c

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@
2222

2323
struct numa_memory_target_t {
2424
size_t id;
25-
26-
// saved pointer to the global base allocator
27-
umf_ba_pool_t *base_allocator;
2825
};
2926

3027
static umf_result_t numa_initialize(void *params, void **memTarget) {
@@ -35,27 +32,19 @@ static umf_result_t numa_initialize(void *params, void **memTarget) {
3532
struct umf_numa_memory_target_config_t *config =
3633
(struct umf_numa_memory_target_config_t *)params;
3734

38-
umf_ba_pool_t *base_allocator =
39-
umf_ba_get_pool(sizeof(struct numa_memory_target_t));
40-
if (!base_allocator) {
41-
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
42-
}
43-
44-
struct numa_memory_target_t *numaTarget = umf_ba_alloc(base_allocator);
35+
struct numa_memory_target_t *numaTarget =
36+
umf_ba_global_alloc(sizeof(struct numa_memory_target_t));
4537
if (!numaTarget) {
4638
return UMF_RESULT_ERROR_OUT_OF_HOST_MEMORY;
4739
}
4840

49-
numaTarget->base_allocator = base_allocator;
5041
numaTarget->id = config->id;
5142
*memTarget = numaTarget;
5243
return UMF_RESULT_SUCCESS;
5344
}
5445

5546
static void numa_finalize(void *memTarget) {
56-
struct numa_memory_target_t *numaTarget =
57-
(struct numa_memory_target_t *)memTarget;
58-
umf_ba_free(numaTarget->base_allocator, memTarget);
47+
umf_ba_global_free(memTarget, sizeof(struct numa_memory_target_t));
5948
}
6049

6150
// sets maxnode and allocates and initializes mask based on provided memory targets

src/memspace.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <umf/memspace.h>
1414

15+
#include "base_alloc_global.h"
1516
#include "memory_target.h"
1617
#include "memory_target_ops.h"
1718
#include "memspace_internal.h"
@@ -105,5 +106,5 @@ void umfMemspaceDestroy(umf_memspace_handle_t memspace) {
105106
}
106107

107108
umf_ba_linear_destroy(memspace->linear_allocator);
108-
umf_ba_free(memspace->base_allocator, memspace);
109+
umf_ba_global_free(memspace, sizeof(struct umf_memspace_t));
109110
}

src/memspace_internal.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ struct umf_memspace_t {
2424
size_t size;
2525
umf_memory_target_handle_t *nodes;
2626

27-
// saved pointer to the global base allocator
28-
umf_ba_pool_t *base_allocator;
29-
3027
// own local linear base allocator
3128
umf_ba_linear_pool_t *linear_allocator;
3229
};

0 commit comments

Comments
 (0)