-
Notifications
You must be signed in to change notification settings - Fork 33
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
make disjoint pool a C structure #898
base: main
Are you sure you want to change the base?
Conversation
97807f1
to
3521be5
Compare
17d00a2
to
9b22721
Compare
cdad0f1
to
4a8935a
Compare
1bf955a
to
0fa2440
Compare
706aa96
to
b2d07ad
Compare
@lplewa please review :) |
0d2e129
to
c87e492
Compare
c87e492
to
5764115
Compare
5764115
to
e8bc871
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be continued ...
|
||
// Represents the allocated memory block of size 'slab_min_size' | ||
// Internally, it splits the memory block into chunks. The number of | ||
// chunks depends of the size of a Bucket which created the Slab. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
depends on
size_t slab_size; | ||
|
||
// Represents the current state of each chunk: if the bit is set then the | ||
// chunk is allocated, and if the chunk is free for allocation otherwise |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or the chunk is free ...
slab_list_item_t *available_slabs; | ||
size_t available_slabs_num; | ||
|
||
// Linked list of slabs with 0 available chunk. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... chunks.
// When a slab becomes entirely free we have to decide whether to return it | ||
// to the provider or keep it allocated. A simple check for size of the | ||
// Available list is not sufficient to check whether any slab has been | ||
// pooled yet.We would have to traverse the entire Available listand check |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
listand
-> list and
// For buckets used in chunked mode, a counter of slabs in the pool. | ||
// For allocations that use an entire slab each, the entries in the Available | ||
// list are entries in the pool.Each slab is available for a new | ||
// allocation.The size of the Available list is the size of the pool. | ||
// For allocations that use slabs in chunked mode, slabs will be in the | ||
// Available list if any one or more of their chunks is free.The entire slab | ||
// is not necessarily free, just some chunks in the slab are free. To | ||
// implement pooling we will allow one slab in the Available list to be | ||
// entirely empty. Normally such a slab would have been freed. But | ||
// now we don't, and treat this slab as "in the pool". | ||
// When a slab becomes entirely free we have to decide whether to return it | ||
// to the provider or keep it allocated. A simple check for size of the | ||
// Available list is not sufficient to check whether any slab has been | ||
// pooled yet.We would have to traverse the entire Available listand check | ||
// if any of them is entirely free. Instead we keep a counter of entirely | ||
// empty slabs within the Available list to speed up the process of checking | ||
// if a slab in this bucket is already pooled. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use space
after dot .
} | ||
|
||
bool slab_has_avail(const slab_t *slab) { | ||
return slab->num_allocated != slab->num_chunks; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return slab->num_allocated < slab->num_chunks;
// points to the last byte of slab data | ||
void *slab_addr = slab_get(slab); | ||
ASSERT_IS_ALIGNED((uintptr_t)slab_addr, bucket_slab_min_size(bucket)); | ||
LOG_DEBUG("slab: %p, start: %p", (void *)slab, slab_addr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOG_DEBUG("registering slab: %p, ...
int ret = critnib_insert(slabs, (uintptr_t)slab_addr, slab, 0); | ||
if (ret == ENOMEM) { | ||
LOG_ERR("register failed because of out of memory!"); | ||
} else if (ret == EEXIST) { | ||
LOG_ERR("register failed because the address is already registered!"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function returns void, so the caller does not know if registering failed or succeeded.
|
||
void *slab_addr = slab_get(slab); | ||
ASSERT_IS_ALIGNED((uintptr_t)slab_addr, bucket_slab_min_size(bucket)); | ||
LOG_DEBUG("slab: %p, start: %p", (void *)slab, slab_addr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LOG_DEBUG("unregistering slab: %p, ...
utils_write_lock(lock); | ||
|
||
#ifndef NDEBUG | ||
// debug only |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redundant
@@ -258,10 +257,6 @@ To enable this feature, the `UMF_BUILD_SHARED_LIBRARY` option needs to be turned | |||
|
|||
TODO: Add a description |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you pls add some description here?
and also mention that it is (part of libumf)
now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about all debug_logs. Imho there are litte too much logs for production code, so maybe we should try to reduce them to maximum 1-2 logs per API call.
if (mutex_internal->lock.RecursionCount > 1) { | ||
LeaveCriticalSection(&mutex_internal->lock); | ||
if (mutex->lock.RecursionCount > 1) { | ||
LeaveCriticalSection(&mutex->lock); | ||
/* deadlock detected */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a critical error and it should abort program
// Temporary solution for disabling memory poisoning. This is needed because | ||
// AddressSanitizer does not support memory poisoning for GPU allocations. | ||
// More info: https://github.com/oneapi-src/unified-memory-framework/issues/634 | ||
#ifndef POISON_MEMORY | ||
#define POISON_MEMORY 0 | ||
#endif | ||
|
||
static void annotate_memory_inaccessible(void *ptr, size_t size) { | ||
(void)ptr; | ||
(void)size; | ||
#if (POISON_MEMORY != 0) | ||
utils_annotate_memory_inaccessible(ptr, size); | ||
#endif | ||
} | ||
|
||
static void annotate_memory_undefined(void *ptr, size_t size) { | ||
(void)ptr; | ||
(void)size; | ||
#if (POISON_MEMORY != 0) | ||
utils_annotate_memory_undefined(ptr, size); | ||
#endif | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Temporary solution for disabling memory poisoning. This is needed because | |
// AddressSanitizer does not support memory poisoning for GPU allocations. | |
// More info: https://github.com/oneapi-src/unified-memory-framework/issues/634 | |
#ifndef POISON_MEMORY | |
#define POISON_MEMORY 0 | |
#endif | |
static void annotate_memory_inaccessible(void *ptr, size_t size) { | |
(void)ptr; | |
(void)size; | |
#if (POISON_MEMORY != 0) | |
utils_annotate_memory_inaccessible(ptr, size); | |
#endif | |
} | |
static void annotate_memory_undefined(void *ptr, size_t size) { | |
(void)ptr; | |
(void)size; | |
#if (POISON_MEMORY != 0) | |
utils_annotate_memory_undefined(ptr, size); | |
#endif | |
} | |
// Temporary solution for disabling memory poisoning. This is needed because | |
// AddressSanitizer does not support memory poisoning for GPU allocations. | |
// More info: https://github.com/oneapi-src/unified-memory-framework/issues/634 | |
#ifndef POISON_MEMORY | |
#undef __SANITIZE_ADDRESS__ | |
#endif | |
#include "utils_sanitizers.h" | |
} |
slab->bucket = bucket; | ||
|
||
slab->iter = | ||
(slab_list_item_t *)umf_ba_global_alloc(sizeof(slab_list_item_t)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is no longer c++. Cast is not needed
sizeof(slab->iter )
slab_t *create_slab(bucket_t *bucket, bool full_size) { | ||
assert(bucket); | ||
|
||
slab_t *slab = umf_ba_global_alloc(sizeof(slab_t)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sizeof(*slab)
slab->chunks = umf_ba_global_alloc(sizeof(bool) * slab->num_chunks); | ||
if (slab->chunks == NULL) { | ||
LOG_ERR("allocation of slab chunks failed!"); | ||
umf_ba_global_free(slab->iter); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please use goto to handle errors
#ifndef NDEBUG | ||
// debug check | ||
bucket_t *bucket = pool->buckets[calculated_idx]; | ||
assert(bucket->size >= size); | ||
(void)bucket; | ||
|
||
if (calculated_idx > 0) { | ||
bucket_t *bucket_prev = pool->buckets[calculated_idx - 1]; | ||
assert(bucket_prev->size < size); | ||
(void)bucket_prev; | ||
} | ||
#endif // NDEBUG |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If buckets are created on pool creation, do we need to check their sizes and order each time that we are looking for correct one?
void bucket_print_stats(bucket_t *bucket, bool *title_printed, | ||
const char *label) { | ||
if (bucket->alloc_count) { | ||
if (!*title_printed) { | ||
LOG_DEBUG("\"%s\" pool memory statistics", label); | ||
LOG_DEBUG("%14s %12s %12s %18s %20s %21s", "Bucket Size", "Allocs", | ||
"Frees", "Allocs from Pool", "Peak Slabs in Use", | ||
"Peak Slabs in Pool"); | ||
*title_printed = true; | ||
} | ||
LOG_DEBUG("%14zu %12zu %12zu %18zu %20zu %21zu", bucket->size, | ||
bucket->alloc_count, bucket->free_count, | ||
bucket->alloc_pool_count, bucket->max_slabs_in_use, | ||
bucket->max_slabs_in_pool); | ||
} | ||
} | ||
|
||
void disjoint_pool_print_stats(disjoint_pool_t *pool, bool *title_printed, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of bool *title_printed, create function print_title and just print once, without extra pointer argument passed to all functions.
|
||
disjoint_pool_print_stats(hPool, &title_printed, &high_bucket_size, | ||
&high_peak_slabs_in_use, name); | ||
if (title_printed) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will be allways printed, so not sure why we have if here
} | ||
|
||
disjoint_pool_t *disjoint_pool = | ||
(disjoint_pool_t *)umf_ba_global_alloc(sizeof(struct disjoint_pool_t)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not needed cast
sizeof(*disjoint_pool)
|
||
umf_result_t disjoint_pool_initialize(umf_memory_provider_handle_t provider, | ||
void *params, void **ppPool) { | ||
if (!provider) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check other arguments
|
||
umf_disjoint_pool_shared_limits_handle_t shared_limits; | ||
|
||
// For buckets used in chunked mode, a counter of slabs in the pool. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mixing description of chunked slabs with non-chunked brings confusion in this comment. Please separate these cases, i.e. by creating two blocks of text, one for each case.
// Available list if any one or more of their chunks is free.The entire slab | ||
// is not necessarily free, just some chunks in the slab are free. To | ||
// implement pooling we will allow one slab in the Available list to be | ||
// entirely empty. Normally such a slab would have been freed. But |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to describe what API does, not what it doesn't do, to avoid confusion. Normally(..)
sentence could be dropped. Same below before Instead(...)
.
// Protects the bucket and all the corresponding slabs | ||
utils_mutex_t bucket_lock; | ||
|
||
// Reference to the allocator context, used access memory allocation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used to access ?
umf_result_t | ||
umfDisjointPoolParamsDestroy(umf_disjoint_pool_params_handle_t hParams) { | ||
// NOTE: dereferencing hParams when BA is already destroyed leads to crash | ||
if (hParams && !umf_ba_global_is_destroyed()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shall there be some warning when base allocator is already destroyed?
disjoint_pool_print_stats(hPool, &title_printed, &high_bucket_size, | ||
&high_peak_slabs_in_use, name); | ||
if (title_printed) { | ||
LOG_DEBUG("current pool size: %zu", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
current -> destroyed ?
if (title_printed) { | ||
LOG_DEBUG("current pool size: %zu", | ||
disjoint_pool_get_limits(hPool)->total_size); | ||
LOG_DEBUG("suggested setting=;%c%s:%zu,%zu,64K", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are destroying a pool, what do we need suggested settings for?
umf_result_t ret = | ||
umfMemoryProviderAlloc(pool->provider, size, 0, &ptr); | ||
if (ret != UMF_RESULT_SUCCESS) { | ||
TLS_last_allocation_error = ret; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Log a failure?
int i = 0; | ||
Size1 = ts1; | ||
Size2 = ts2; | ||
for (; Size2 < CutOff; Size1 *= 2, Size2 *= 2, i += 2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The last Size1
bucket won't be created in case 1.5 * Size1 < CutOff
even if Size1 < CutOff
. This should be either created or clearly commented somewhere that this is the case.
|
||
void *disjoint_pool_malloc(void *pool, size_t size) { | ||
disjoint_pool_t *hPool = (disjoint_pool_t *)pool; | ||
void *ptr = disjoint_pool_allocate(hPool, size); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check for pool == NULL
// This allocation will be served from a Bucket which size is multiple | ||
// of Alignment and Slab address is aligned to provider_min_page_size | ||
// so the address will be properly aligned. | ||
aligned_size = (size > 1) ? ALIGN_UP(size, alignment) : alignment; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ALIGN_UP_SAFE
?
while (chunk != slab->chunks + slab->num_chunks) { | ||
// false means not used | ||
if (*chunk == false) { | ||
size_t idx = (chunk - slab->chunks) / sizeof(bool); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bool *
pointer arithmetic will already provide the index number. Remove the division by sizeof(bool)
.
make Disjoint Pool a C structure. With this PR: