Add a per-arena oversize_threshold.
This can let manual arenas trade off memory and CPU the way auto arenas do.
This commit is contained in:
committed by
David Goldblatt
parent
4ca3d91e96
commit
cf2549a149
131
test/unit/oversize_threshold.c
Normal file
131
test/unit/oversize_threshold.c
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "test/jemalloc_test.h"
|
||||
|
||||
#include "jemalloc/internal/ctl.h"
|
||||
|
||||
static void
|
||||
arena_mallctl(const char *mallctl_str, unsigned arena, void *oldp,
|
||||
size_t *oldlen, void *newp, size_t newlen) {
|
||||
int err;
|
||||
char buf[100];
|
||||
malloc_snprintf(buf, sizeof(buf), mallctl_str, arena);
|
||||
|
||||
err = mallctl(buf, oldp, oldlen, newp, newlen);
|
||||
expect_d_eq(0, err, "Mallctl failed; %s", buf);
|
||||
}
|
||||
|
||||
TEST_BEGIN(test_oversize_threshold_get_set) {
|
||||
int err;
|
||||
size_t old_threshold;
|
||||
size_t new_threshold;
|
||||
size_t threshold_sz = sizeof(old_threshold);
|
||||
|
||||
unsigned arena;
|
||||
size_t arena_sz = sizeof(arena);
|
||||
err = mallctl("arenas.create", (void *)&arena, &arena_sz, NULL, 0);
|
||||
expect_d_eq(0, err, "Arena creation failed");
|
||||
|
||||
/* Just a write. */
|
||||
new_threshold = 1024 * 1024;
|
||||
arena_mallctl("arena.%u.oversize_threshold", arena, NULL, NULL,
|
||||
&new_threshold, threshold_sz);
|
||||
|
||||
/* Read and write */
|
||||
new_threshold = 2 * 1024 * 1024;
|
||||
arena_mallctl("arena.%u.oversize_threshold", arena, &old_threshold,
|
||||
&threshold_sz, &new_threshold, threshold_sz);
|
||||
expect_zu_eq(1024 * 1024, old_threshold, "Should have read old value");
|
||||
|
||||
/* Just a read */
|
||||
arena_mallctl("arena.%u.oversize_threshold", arena, &old_threshold,
|
||||
&threshold_sz, NULL, 0);
|
||||
expect_zu_eq(2 * 1024 * 1024, old_threshold, "Should have read old value");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
static size_t max_purged = 0;
|
||||
static bool
|
||||
purge_forced_record_max(extent_hooks_t* hooks, void *addr, size_t sz,
|
||||
size_t offset, size_t length, unsigned arena_ind) {
|
||||
if (length > max_purged) {
|
||||
max_purged = length;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
dalloc_record_max(extent_hooks_t *extent_hooks, void *addr, size_t sz,
|
||||
bool comitted, unsigned arena_ind) {
|
||||
if (sz > max_purged) {
|
||||
max_purged = sz;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
extent_hooks_t max_recording_extent_hooks;
|
||||
|
||||
TEST_BEGIN(test_oversize_threshold) {
|
||||
max_recording_extent_hooks = ehooks_default_extent_hooks;
|
||||
max_recording_extent_hooks.purge_forced = &purge_forced_record_max;
|
||||
max_recording_extent_hooks.dalloc = &dalloc_record_max;
|
||||
|
||||
extent_hooks_t *extent_hooks = &max_recording_extent_hooks;
|
||||
|
||||
int err;
|
||||
|
||||
unsigned arena;
|
||||
size_t arena_sz = sizeof(arena);
|
||||
err = mallctl("arenas.create", (void *)&arena, &arena_sz, NULL, 0);
|
||||
expect_d_eq(0, err, "Arena creation failed");
|
||||
arena_mallctl("arena.%u.extent_hooks", arena, NULL, NULL, &extent_hooks,
|
||||
sizeof(extent_hooks));
|
||||
|
||||
/*
|
||||
* This test will fundamentally race with purging, since we're going to
|
||||
* check the dirty stats to see if our oversized allocation got purged.
|
||||
* We don't want other purging to happen accidentally. We can't just
|
||||
* disable purging entirely, though, since that will also disable
|
||||
* oversize purging. Just set purging intervals to be very large.
|
||||
*/
|
||||
ssize_t decay_ms = 100 * 1000;
|
||||
ssize_t decay_ms_sz = sizeof(decay_ms);
|
||||
arena_mallctl("arena.%u.dirty_decay_ms", arena, NULL, NULL, &decay_ms,
|
||||
decay_ms_sz);
|
||||
arena_mallctl("arena.%u.muzzy_decay_ms", arena, NULL, NULL, &decay_ms,
|
||||
decay_ms_sz);
|
||||
|
||||
/* Clean everything out. */
|
||||
arena_mallctl("arena.%u.purge", arena, NULL, NULL, NULL, 0);
|
||||
max_purged = 0;
|
||||
|
||||
/* Set threshold to 1MB. */
|
||||
size_t threshold = 1024 * 1024;
|
||||
size_t threshold_sz = sizeof(threshold);
|
||||
arena_mallctl("arena.%u.oversize_threshold", arena, NULL, NULL,
|
||||
&threshold, threshold_sz);
|
||||
|
||||
/* Allocating and freeing half a megabyte should leave them dirty. */
|
||||
void *ptr = mallocx(512 * 1024, MALLOCX_ARENA(arena));
|
||||
dallocx(ptr, MALLOCX_TCACHE_NONE);
|
||||
expect_zu_lt(max_purged, 512 * 1024, "Expected no 512k purge");
|
||||
|
||||
/* Purge again to reset everything out. */
|
||||
arena_mallctl("arena.%u.purge", arena, NULL, NULL, NULL, 0);
|
||||
max_purged = 0;
|
||||
|
||||
/*
|
||||
* Allocating and freeing 2 megabytes should leave them dirty because of
|
||||
* the oversize threshold.
|
||||
*/
|
||||
ptr = mallocx(2 * 1024 * 1024, MALLOCX_ARENA(arena));
|
||||
dallocx(ptr, MALLOCX_TCACHE_NONE);
|
||||
expect_zu_ge(max_purged, 2 * 1024 * 1024, "Expected a 2MB purge");
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void) {
|
||||
return test_no_reentrancy(
|
||||
test_oversize_threshold_get_set,
|
||||
test_oversize_threshold);
|
||||
}
|
||||
|
@@ -63,9 +63,11 @@ test_data_t *init_test_data(ssize_t dirty_decay_ms, ssize_t muzzy_decay_ms) {
|
||||
nstime_t time;
|
||||
nstime_init(&time, 0);
|
||||
|
||||
const size_t oversize_threshold = 8 * 1024 * 1024;
|
||||
err = pa_shard_init(TSDN_NULL, &test_data->shard, &test_data->emap,
|
||||
test_data->base, /* ind */ 1, &test_data->stats,
|
||||
&test_data->stats_mtx, &time, dirty_decay_ms, muzzy_decay_ms);
|
||||
&test_data->stats_mtx, &time, oversize_threshold, dirty_decay_ms,
|
||||
muzzy_decay_ms);
|
||||
assert_false(err, "");
|
||||
|
||||
return test_data;
|
||||
|
Reference in New Issue
Block a user