Skip to content

Commit 047432a

Browse files
authored
Merge pull request #1318 from bratpiorka/rrudnick_dp_trim
add umfPoolTrimMemory
2 parents b56f690 + 7930e59 commit 047432a

18 files changed

+326
-27
lines changed

docs/config/spelling_exceptions.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Memtarget
3535
memtarget
3636
memtargets
3737
middleware
38+
minBytesToKeep
3839
multithreading
3940
Nodemask
4041
nodemask

include/umf/memory_pool.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,24 @@ umf_result_t umfPoolSetTag(umf_memory_pool_handle_t hPool, void *tag,
196196
/// @return UMF_RESULT_SUCCESS on success.
197197
umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag);
198198

199+
///
200+
/// @brief Trims memory of the pool, removing resources that are not needed
201+
/// to keep the pool operational.
202+
/// \details
203+
/// The minBytesToKeep parameter is a hint to the pool implementation
204+
/// that it should try to keep at least this number of bytes of
205+
/// memory in the pool. The pool implementation may also ignore this
206+
/// parameter and try to trim the whole memory, in which case it
207+
/// should return UMF_RESULT_SUCCESS. The pool implementation may
208+
/// also return UMF_RESULT_ERROR_NOT_SUPPORTED if it does not support
209+
/// trimming memory.
210+
/// @param hPool pointer to the memory pool
211+
/// @param minBytesToKeep minimum number of bytes to keep in the pool (if
212+
/// possible - see details)
213+
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on failure.
214+
umf_result_t umfPoolTrimMemory(umf_memory_pool_handle_t hPool,
215+
size_t minBytesToKeep);
216+
199217
#ifdef __cplusplus
200218
}
201219
#endif

include/umf/memory_pool_ops.h

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extern "C" {
2222
/// @brief Version of the Memory Pool ops structure.
2323
/// NOTE: This is equal to the latest UMF version, in which the ops structure
2424
/// has been modified.
25-
#define UMF_POOL_OPS_VERSION_CURRENT UMF_MAKE_VERSION(1, 0)
25+
#define UMF_POOL_OPS_VERSION_CURRENT UMF_MAKE_VERSION(1, 1)
2626

2727
///
2828
/// @brief This structure comprises function pointers used by corresponding umfPool*
@@ -143,7 +143,7 @@ typedef struct umf_memory_pool_ops_t {
143143
umf_result_t (*get_name)(void *pool, const char **name);
144144

145145
///
146-
/// The following function is optional and memory pool implementation
146+
/// The following functions are optional and memory pool implementation
147147
/// can keep it NULL.
148148
///
149149

@@ -166,6 +166,26 @@ typedef struct umf_memory_pool_ops_t {
166166
const char *name, void *arg, size_t size,
167167
umf_ctl_query_type_t queryType, va_list args);
168168

169+
// The following operations were added in ops version 1.1
170+
171+
///
172+
/// @brief Trims memory of the pool, removing resources that are not needed
173+
/// to keep the pool operational.
174+
/// \details
175+
/// The minBytesToKeep parameter is a hint to the pool implementation
176+
/// that it should try to keep at least this number of bytes of
177+
/// memory in the pool. The pool implementation may also ignore this
178+
/// parameter and try to trim the whole memory, in which case it
179+
/// should return UMF_RESULT_SUCCESS. The pool implementation may
180+
/// also return UMF_RESULT_ERROR_NOT_SUPPORTED if it does not support
181+
/// trimming memory.
182+
/// @param pool pointer to the memory pool
183+
/// @param minBytesToKeep minimum number of bytes to keep in the pool (if
184+
/// possible - see details)
185+
/// @return UMF_RESULT_SUCCESS on success or appropriate error code on
186+
/// failure.
187+
///
188+
umf_result_t (*ext_trim_memory)(void *pool, size_t minBytesToKeep);
169189
} umf_memory_pool_ops_t;
170190

171191
#ifdef __cplusplus

src/libumf.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,5 @@ EXPORTS
152152
umfJemallocPoolParamsSetName
153153
umfLevelZeroMemoryProviderParamsSetName
154154
umfOsMemoryProviderParamsSetName
155+
umfPoolTrimMemory
155156
umfScalablePoolParamsSetName

src/libumf.map

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,6 @@ UMF_1.1 {
150150
umfJemallocPoolParamsSetName;
151151
umfLevelZeroMemoryProviderParamsSetName;
152152
umfOsMemoryProviderParamsSetName;
153+
umfPoolTrimMemory;
153154
umfScalablePoolParamsSetName;
154155
} UMF_1.0;

src/memory_pool.c

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,13 @@ umfDefaultCtlPoolHandle(void *hPool, umf_ctl_query_source_t operationType,
212212
return UMF_RESULT_ERROR_NOT_SUPPORTED;
213213
}
214214

215+
static umf_result_t umfDefaultTrimMemory(void *provider,
216+
size_t minBytesToKeep) {
217+
(void)provider;
218+
(void)minBytesToKeep;
219+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
220+
}
221+
215222
// logical sum (OR) of all umf_pool_create_flags_t flags
216223
static const umf_pool_create_flags_t UMF_POOL_CREATE_FLAG_ALL =
217224
UMF_POOL_CREATE_FLAG_OWN_PROVIDER | UMF_POOL_CREATE_FLAG_DISABLE_TRACKING;
@@ -233,9 +240,9 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
233240
const void *params,
234241
umf_pool_create_flags_t flags,
235242
umf_memory_pool_handle_t *hPool) {
236-
if (!ops || !provider || !hPool) {
237-
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
238-
}
243+
UMF_CHECK((ops != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
244+
UMF_CHECK((provider != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
245+
UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
239246

240247
// validate flags
241248
if (flags & ~UMF_POOL_CREATE_FLAG_ALL) {
@@ -245,10 +252,24 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
245252

246253
umf_result_t ret = UMF_RESULT_SUCCESS;
247254

255+
umf_memory_pool_ops_t compatible_ops;
248256
if (ops->version != UMF_POOL_OPS_VERSION_CURRENT) {
249257
LOG_WARN("Memory Pool ops version \"%d\" is different than the current "
250258
"version \"%d\"",
251259
ops->version, UMF_POOL_OPS_VERSION_CURRENT);
260+
261+
// Create a new ops compatible structure with the current version
262+
memset(&compatible_ops, 0, sizeof(compatible_ops));
263+
if (UMF_MINOR_VERSION(ops->version) == 0) {
264+
LOG_INFO("Detected 1.0 version of Memory Pool ops, "
265+
"upgrading to current version");
266+
memcpy(&compatible_ops, ops,
267+
offsetof(umf_memory_pool_ops_t, ext_trim_memory));
268+
} else {
269+
LOG_ERR("Unsupported Memory Pool ops version: %d", ops->version);
270+
return UMF_RESULT_ERROR_NOT_SUPPORTED;
271+
}
272+
ops = &compatible_ops;
252273
}
253274

254275
umf_memory_pool_handle_t pool =
@@ -278,6 +299,10 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
278299
pool->ops.ext_ctl = umfDefaultCtlPoolHandle;
279300
}
280301

302+
if (NULL == pool->ops.ext_trim_memory) {
303+
pool->ops.ext_trim_memory = umfDefaultTrimMemory;
304+
}
305+
281306
if (NULL == utils_mutex_init(&pool->lock)) {
282307
LOG_ERR("Failed to initialize mutex for pool");
283308
ret = UMF_RESULT_ERROR_UNKNOWN;
@@ -326,10 +351,7 @@ static umf_result_t umfPoolCreateInternal(const umf_memory_pool_ops_t *ops,
326351
}
327352

328353
umf_result_t umfPoolDestroy(umf_memory_pool_handle_t hPool) {
329-
if (hPool == NULL) {
330-
LOG_ERR("memory pool handle is NULL");
331-
return UMF_RESULT_ERROR_INVALID_ARGUMENT;
332-
}
354+
UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
333355

334356
if (umf_ba_global_is_destroyed()) {
335357
return UMF_RESULT_ERROR_UNKNOWN;
@@ -509,6 +531,13 @@ umf_result_t umfPoolGetTag(umf_memory_pool_handle_t hPool, void **tag) {
509531
return UMF_RESULT_SUCCESS;
510532
}
511533

534+
umf_result_t umfPoolTrimMemory(umf_memory_pool_handle_t hPool,
535+
size_t minBytesToKeep) {
536+
UMF_CHECK((hPool != NULL), UMF_RESULT_ERROR_INVALID_ARGUMENT);
537+
538+
return hPool->ops.ext_trim_memory(hPool->pool_priv, minBytesToKeep);
539+
}
540+
512541
void umfPoolCtlDefaultsDestroy(void) {
513542
utils_init_once(&mem_pool_ctl_initialized, pool_ctl_init);
514543

src/pool/pool_disjoint.c

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,11 +444,11 @@ static void bucket_free_chunk(bucket_t *bucket, void *ptr, slab_t *slab,
444444
// remove slab
445445
slab_list_item_t *slab_it = &slab->iter;
446446
assert(slab_it->val != NULL);
447-
pool_unregister_slab(bucket->pool, slab_it->val);
447+
destroy_slab(slab_it->val);
448448
DL_DELETE(bucket->available_slabs, slab_it);
449449
assert(bucket->available_slabs_num > 0);
450450
bucket->available_slabs_num--;
451-
destroy_slab(slab_it->val);
451+
pool_unregister_slab(bucket->pool, slab_it->val);
452452
}
453453
} else {
454454
// return this chunk to the pool
@@ -1133,6 +1133,47 @@ static umf_result_t disjoint_pool_get_name(void *pool, const char **name) {
11331133
return UMF_RESULT_SUCCESS;
11341134
}
11351135

1136+
umf_result_t disjoint_pool_trim_memory(void *pool, size_t minBytesToKeep) {
1137+
assert(pool != NULL);
1138+
disjoint_pool_t *hPool = (disjoint_pool_t *)pool;
1139+
1140+
for (size_t i = 0; i < hPool->buckets_num; i++) {
1141+
bucket_t *bucket = hPool->buckets[i];
1142+
utils_mutex_lock(&bucket->bucket_lock);
1143+
1144+
// remove empty slabs from the pool
1145+
slab_list_item_t *it = NULL, *tmp = NULL;
1146+
LL_FOREACH_SAFE(bucket->available_slabs, it, tmp) {
1147+
slab_t *slab = it->val;
1148+
if (slab->num_chunks_allocated == 0) {
1149+
if (minBytesToKeep > 0) {
1150+
// if we still have bytes to keep, do not remove slab
1151+
if (minBytesToKeep > slab->slab_size) {
1152+
minBytesToKeep -= slab->slab_size;
1153+
} else {
1154+
minBytesToKeep = 0;
1155+
}
1156+
continue;
1157+
}
1158+
1159+
// remove slab
1160+
destroy_slab(slab);
1161+
DL_DELETE(bucket->available_slabs, it);
1162+
assert(bucket->available_slabs_num > 0);
1163+
bucket->available_slabs_num--;
1164+
pool_unregister_slab(hPool, slab);
1165+
1166+
// update stats
1167+
bucket_update_stats(bucket, 0, -1);
1168+
}
1169+
}
1170+
1171+
utils_mutex_unlock(&bucket->bucket_lock);
1172+
}
1173+
1174+
return UMF_RESULT_SUCCESS;
1175+
}
1176+
11361177
static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = {
11371178
.version = UMF_POOL_OPS_VERSION_CURRENT,
11381179
.initialize = disjoint_pool_initialize,
@@ -1146,6 +1187,7 @@ static umf_memory_pool_ops_t UMF_DISJOINT_POOL_OPS = {
11461187
.get_last_allocation_error = disjoint_pool_get_last_allocation_error,
11471188
.get_name = disjoint_pool_get_name,
11481189
.ext_ctl = disjoint_pool_ctl,
1190+
.ext_trim_memory = disjoint_pool_trim_memory,
11491191
};
11501192

11511193
const umf_memory_pool_ops_t *umfDisjointPoolOps(void) {

src/pool/pool_jemalloc.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,28 @@ static umf_result_t op_get_name(void *pool, const char **name) {
588588
return UMF_RESULT_SUCCESS;
589589
}
590590

591+
static umf_result_t op_trim_memory(void *pool, size_t minBytesToKeep) {
592+
// there is no way to tell jemalloc to keep a minimum number of bytes
593+
// so we just purge all arenas
594+
if (minBytesToKeep > 0) {
595+
LOG_WARN("Ignoring minBytesToKeep (%zu) in jemalloc pool",
596+
minBytesToKeep);
597+
}
598+
599+
jemalloc_memory_pool_t *je_pool = (jemalloc_memory_pool_t *)pool;
600+
for (size_t i = 0; i < je_pool->n_arenas; i++) {
601+
char cmd[64];
602+
unsigned arena = je_pool->arena_index[i];
603+
snprintf(cmd, sizeof(cmd), "arena.%u.purge", arena);
604+
if (je_mallctl(cmd, NULL, NULL, NULL, 0)) {
605+
LOG_ERR("Could not purge jemalloc arena %u", arena);
606+
return UMF_RESULT_ERROR_UNKNOWN;
607+
}
608+
}
609+
610+
return UMF_RESULT_SUCCESS;
611+
}
612+
591613
static umf_memory_pool_ops_t UMF_JEMALLOC_POOL_OPS = {
592614
.version = UMF_POOL_OPS_VERSION_CURRENT,
593615
.initialize = op_initialize,
@@ -600,6 +622,7 @@ static umf_memory_pool_ops_t UMF_JEMALLOC_POOL_OPS = {
600622
.free = op_free,
601623
.get_last_allocation_error = op_get_last_allocation_error,
602624
.get_name = op_get_name,
625+
.ext_trim_memory = op_trim_memory,
603626
};
604627

605628
const umf_memory_pool_ops_t *umfJemallocPoolOps(void) {

src/pool/pool_proxy.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,9 @@ static umf_memory_pool_ops_t UMF_PROXY_POOL_OPS = {
147147
.malloc_usable_size = proxy_malloc_usable_size,
148148
.free = proxy_free,
149149
.get_last_allocation_error = proxy_get_last_allocation_error,
150-
.get_name = proxy_get_name};
150+
.get_name = proxy_get_name,
151+
.ext_trim_memory = NULL, // not supported
152+
};
151153

152154
const umf_memory_pool_ops_t *umfProxyPoolOps(void) {
153155
return &UMF_PROXY_POOL_OPS;

src/pool/pool_scalable.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,12 +450,14 @@ static umf_result_t tbb_get_last_allocation_error(void *pool) {
450450
return TLS_last_allocation_error;
451451
}
452452

453+
static void initialize_pool_ctl(void) {}
454+
453455
static umf_result_t pool_ctl(void *hPool, umf_ctl_query_source_t operationType,
454456
const char *name, void *arg, size_t size,
455457
umf_ctl_query_type_t query_type, va_list args) {
456458
(void)operationType; // unused
457459
umf_memory_pool_handle_t pool_provider = (umf_memory_pool_handle_t)hPool;
458-
utils_init_once(&ctl_initialized, NULL);
460+
utils_init_once(&ctl_initialized, initialize_pool_ctl);
459461
return ctl_query(&pool_scallable_ctl_root, pool_provider->pool_priv,
460462
CTL_QUERY_PROGRAMMATIC, name, query_type, arg, size, args);
461463
}
@@ -486,6 +488,7 @@ static umf_memory_pool_ops_t UMF_SCALABLE_POOL_OPS = {
486488
.get_last_allocation_error = tbb_get_last_allocation_error,
487489
.ext_ctl = pool_ctl,
488490
.get_name = scalable_get_name,
491+
.ext_trim_memory = NULL, // not supported
489492
};
490493

491494
const umf_memory_pool_ops_t *umfScalablePoolOps(void) {

0 commit comments

Comments
 (0)