Add unit tests for decay
After slight changes in the interface, it's an opportunity to enhance unit tests.
This commit is contained in:
parent
aaea4fd1e6
commit
c88fe355e6
@ -2,10 +2,68 @@
|
|||||||
|
|
||||||
#include "jemalloc/internal/decay.h"
|
#include "jemalloc/internal/decay.h"
|
||||||
|
|
||||||
/*
|
TEST_BEGIN(test_decay_init) {
|
||||||
* Honestly, this is mostly a stub for now. Eventually, we should beef up
|
decay_t decay;
|
||||||
* testing here.
|
memset(&decay, 0, sizeof(decay));
|
||||||
*/
|
|
||||||
|
nstime_t curtime;
|
||||||
|
nstime_init(&curtime, 0);
|
||||||
|
|
||||||
|
ssize_t decay_ms = 1000;
|
||||||
|
assert_true(decay_ms_valid(decay_ms), "");
|
||||||
|
|
||||||
|
expect_false(decay_init(&decay, &curtime, decay_ms),
|
||||||
|
"Failed to initialize decay");
|
||||||
|
expect_zd_eq(decay_ms_read(&decay), decay_ms,
|
||||||
|
"Decay_ms was initialized incorrectly");
|
||||||
|
expect_u64_ne(decay_epoch_duration_ns(&decay), 0,
|
||||||
|
"Epoch duration was initialized incorrectly");
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
TEST_BEGIN(test_decay_ms_valid) {
|
||||||
|
expect_false(decay_ms_valid(-7),
|
||||||
|
"Misclassified negative decay as valid");
|
||||||
|
expect_true(decay_ms_valid(-1),
|
||||||
|
"Misclassified -1 (never decay) as invalid decay");
|
||||||
|
expect_true(decay_ms_valid(8943),
|
||||||
|
"Misclassified valid decay");
|
||||||
|
if (SSIZE_MAX > NSTIME_SEC_MAX) {
|
||||||
|
expect_false(
|
||||||
|
decay_ms_valid((ssize_t)(NSTIME_SEC_MAX * KQU(1000) + 39)),
|
||||||
|
"Misclassified too large decay");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
TEST_BEGIN(test_decay_maybe_advance_epoch) {
|
||||||
|
decay_t decay;
|
||||||
|
memset(&decay, 0, sizeof(decay));
|
||||||
|
|
||||||
|
nstime_t curtime;
|
||||||
|
nstime_init(&curtime, 0);
|
||||||
|
|
||||||
|
uint64_t decay_ms = 1000;
|
||||||
|
|
||||||
|
bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
|
||||||
|
expect_false(err, "");
|
||||||
|
|
||||||
|
bool advanced;
|
||||||
|
advanced = decay_maybe_advance_epoch(&decay, &curtime, 0);
|
||||||
|
expect_false(advanced, "Epoch advanced while time didn't");
|
||||||
|
|
||||||
|
nstime_t interval;
|
||||||
|
nstime_init(&interval, decay_epoch_duration_ns(&decay));
|
||||||
|
|
||||||
|
nstime_add(&curtime, &interval);
|
||||||
|
advanced = decay_maybe_advance_epoch(&decay, &curtime, 0);
|
||||||
|
expect_false(advanced, "Epoch advanced after first interval");
|
||||||
|
|
||||||
|
nstime_add(&curtime, &interval);
|
||||||
|
advanced = decay_maybe_advance_epoch(&decay, &curtime, 0);
|
||||||
|
expect_true(advanced, "Epoch didn't advance after two intervals");
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
TEST_BEGIN(test_decay_empty) {
|
TEST_BEGIN(test_decay_empty) {
|
||||||
/* If we never have any decaying pages, npages_limit should be 0. */
|
/* If we never have any decaying pages, npages_limit should be 0. */
|
||||||
@ -30,16 +88,164 @@ TEST_BEGIN(test_decay_empty) {
|
|||||||
&curtime, dirty_pages);
|
&curtime, dirty_pages);
|
||||||
if (epoch_advanced) {
|
if (epoch_advanced) {
|
||||||
nepochs++;
|
nepochs++;
|
||||||
assert_zu_eq(decay_npages_limit_get(&decay), 0,
|
expect_zu_eq(decay_npages_limit_get(&decay), 0,
|
||||||
"Should not increase the limit arbitrarily");
|
"Unexpectedly increased npages_limit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert_d_gt(nepochs, 0, "Should have advanced epochs");
|
expect_d_gt(nepochs, 0, "Epochs never advanced");
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify that npages_limit correctly decays as the time goes.
|
||||||
|
*
|
||||||
|
* During first 'nepoch_init' epochs, add new dirty pages.
|
||||||
|
* After that, let them decay and verify npages_limit decreases.
|
||||||
|
* Then proceed with another 'nepoch_init' epochs and check that
|
||||||
|
* all dirty pages are flushed out of backlog, bringing npages_limit
|
||||||
|
* down to zero.
|
||||||
|
*/
|
||||||
|
TEST_BEGIN(test_decay) {
|
||||||
|
const uint64_t nepoch_init = 10;
|
||||||
|
|
||||||
|
decay_t decay;
|
||||||
|
memset(&decay, 0, sizeof(decay));
|
||||||
|
|
||||||
|
nstime_t curtime;
|
||||||
|
nstime_init(&curtime, 0);
|
||||||
|
|
||||||
|
uint64_t decay_ms = 1000;
|
||||||
|
uint64_t decay_ns = decay_ms * 1000 * 1000;
|
||||||
|
|
||||||
|
bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
|
||||||
|
assert_false(err, "");
|
||||||
|
|
||||||
|
expect_zu_eq(decay_npages_limit_get(&decay), 0,
|
||||||
|
"Empty decay returned nonzero npages_limit");
|
||||||
|
|
||||||
|
nstime_t epochtime;
|
||||||
|
nstime_init(&epochtime, decay_epoch_duration_ns(&decay));
|
||||||
|
|
||||||
|
const size_t dirty_pages_per_epoch = 1000;
|
||||||
|
size_t dirty_pages = 0;
|
||||||
|
uint64_t epoch_ns = decay_epoch_duration_ns(&decay);
|
||||||
|
bool epoch_advanced = false;
|
||||||
|
|
||||||
|
/* Populate backlog with some dirty pages */
|
||||||
|
for (uint64_t i = 0; i < nepoch_init; i++) {
|
||||||
|
nstime_add(&curtime, &epochtime);
|
||||||
|
dirty_pages += dirty_pages_per_epoch;
|
||||||
|
epoch_advanced |= decay_maybe_advance_epoch(&decay, &curtime,
|
||||||
|
dirty_pages);
|
||||||
|
}
|
||||||
|
expect_true(epoch_advanced, "Epoch never advanced");
|
||||||
|
|
||||||
|
size_t npages_limit = decay_npages_limit_get(&decay);
|
||||||
|
expect_zu_gt(npages_limit, 0, "npages_limit is incorrectly equal "
|
||||||
|
"to zero after dirty pages have been added");
|
||||||
|
|
||||||
|
/* Keep dirty pages unchanged and verify that npages_limit decreases */
|
||||||
|
for (uint64_t i = nepoch_init; i * epoch_ns < decay_ns; ++i) {
|
||||||
|
nstime_add(&curtime, &epochtime);
|
||||||
|
epoch_advanced = decay_maybe_advance_epoch(&decay, &curtime,
|
||||||
|
dirty_pages);
|
||||||
|
if (epoch_advanced) {
|
||||||
|
size_t npages_limit_new = decay_npages_limit_get(&decay);
|
||||||
|
expect_zu_lt(npages_limit_new, npages_limit,
|
||||||
|
"napges_limit failed to decay");
|
||||||
|
|
||||||
|
npages_limit = npages_limit_new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_zu_gt(npages_limit, 0, "npages_limit decayed to zero earlier "
|
||||||
|
"than decay_ms since last dirty page was added");
|
||||||
|
|
||||||
|
/* Completely push all dirty pages out of the backlog */
|
||||||
|
epoch_advanced = false;
|
||||||
|
for (uint64_t i = 0; i < nepoch_init; i++) {
|
||||||
|
nstime_add(&curtime, &epochtime);
|
||||||
|
epoch_advanced |= decay_maybe_advance_epoch(&decay, &curtime,
|
||||||
|
dirty_pages);
|
||||||
|
}
|
||||||
|
expect_true(epoch_advanced, "Epoch never advanced");
|
||||||
|
|
||||||
|
npages_limit = decay_npages_limit_get(&decay);
|
||||||
|
expect_zu_eq(npages_limit, 0, "npages_limit didn't decay to 0 after "
|
||||||
|
"decay_ms since last bump in dirty pages");
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
TEST_BEGIN(test_decay_ns_until_purge) {
|
||||||
|
const uint64_t nepoch_init = 10;
|
||||||
|
|
||||||
|
decay_t decay;
|
||||||
|
memset(&decay, 0, sizeof(decay));
|
||||||
|
|
||||||
|
nstime_t curtime;
|
||||||
|
nstime_init(&curtime, 0);
|
||||||
|
|
||||||
|
uint64_t decay_ms = 1000;
|
||||||
|
uint64_t decay_ns = decay_ms * 1000 * 1000;
|
||||||
|
|
||||||
|
bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
|
||||||
|
assert_false(err, "");
|
||||||
|
|
||||||
|
nstime_t epochtime;
|
||||||
|
nstime_init(&epochtime, decay_epoch_duration_ns(&decay));
|
||||||
|
|
||||||
|
uint64_t ns_until_purge_empty = decay_ns_until_purge(&decay, 0, 0);
|
||||||
|
expect_u64_eq(ns_until_purge_empty, DECAY_UNBOUNDED_TIME_TO_PURGE,
|
||||||
|
"Failed to return unbounded wait time for zero threshold");
|
||||||
|
|
||||||
|
const size_t dirty_pages_per_epoch = 1000;
|
||||||
|
size_t dirty_pages = 0;
|
||||||
|
bool epoch_advanced = false;
|
||||||
|
for (uint64_t i = 0; i < nepoch_init; i++) {
|
||||||
|
nstime_add(&curtime, &epochtime);
|
||||||
|
dirty_pages += dirty_pages_per_epoch;
|
||||||
|
epoch_advanced |= decay_maybe_advance_epoch(&decay, &curtime,
|
||||||
|
dirty_pages);
|
||||||
|
}
|
||||||
|
expect_true(epoch_advanced, "Epoch never advanced");
|
||||||
|
|
||||||
|
uint64_t ns_until_purge_all = decay_ns_until_purge(&decay,
|
||||||
|
dirty_pages, dirty_pages);
|
||||||
|
expect_u64_ge(ns_until_purge_all, decay_ns,
|
||||||
|
"Incorrectly calculated time to purge all pages");
|
||||||
|
|
||||||
|
uint64_t ns_until_purge_none = decay_ns_until_purge(&decay,
|
||||||
|
dirty_pages, 0);
|
||||||
|
expect_u64_eq(ns_until_purge_none, decay_epoch_duration_ns(&decay) * 2,
|
||||||
|
"Incorrectly calculated time to purge 0 pages");
|
||||||
|
|
||||||
|
uint64_t npages_threshold = dirty_pages / 2;
|
||||||
|
uint64_t ns_until_purge_half = decay_ns_until_purge(&decay,
|
||||||
|
dirty_pages, npages_threshold);
|
||||||
|
|
||||||
|
nstime_t waittime;
|
||||||
|
nstime_init(&waittime, ns_until_purge_half);
|
||||||
|
nstime_add(&curtime, &waittime);
|
||||||
|
|
||||||
|
decay_maybe_advance_epoch(&decay, &curtime, dirty_pages);
|
||||||
|
size_t npages_limit = decay_npages_limit_get(&decay);
|
||||||
|
expect_zu_lt(npages_limit, dirty_pages,
|
||||||
|
"npages_limit failed to decrease after waiting");
|
||||||
|
size_t expected = dirty_pages - npages_limit;
|
||||||
|
int deviation = abs((int)expected - (int)(npages_threshold));
|
||||||
|
expect_d_lt(deviation, (int)(npages_threshold / 2),
|
||||||
|
"After waiting, number of pages is out of the expected interval "
|
||||||
|
"[0.5 * npages_threshold .. 1.5 * npages_threshold]");
|
||||||
}
|
}
|
||||||
TEST_END
|
TEST_END
|
||||||
|
|
||||||
int
|
int
|
||||||
main(void) {
|
main(void) {
|
||||||
return test(
|
return test(
|
||||||
test_decay_empty);
|
test_decay_init,
|
||||||
|
test_decay_ms_valid,
|
||||||
|
test_decay_maybe_advance_epoch,
|
||||||
|
test_decay_empty,
|
||||||
|
test_decay,
|
||||||
|
test_decay_ns_until_purge);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user