Home | History | Annotate | Line # | Download | only in unit
      1      1.1  christos #include "test/jemalloc_test.h"
      2      1.1  christos 
      3  1.1.1.2  christos #include "jemalloc/internal/decay.h"
      4      1.1  christos 
      5  1.1.1.2  christos TEST_BEGIN(test_decay_init) {
      6  1.1.1.2  christos 	decay_t decay;
      7  1.1.1.2  christos 	memset(&decay, 0, sizeof(decay));
      8  1.1.1.2  christos 
      9  1.1.1.2  christos 	nstime_t curtime;
     10  1.1.1.2  christos 	nstime_init(&curtime, 0);
     11  1.1.1.2  christos 
     12  1.1.1.2  christos 	ssize_t decay_ms = 1000;
     13  1.1.1.2  christos 	assert_true(decay_ms_valid(decay_ms), "");
     14  1.1.1.2  christos 
     15  1.1.1.2  christos 	expect_false(decay_init(&decay, &curtime, decay_ms),
     16  1.1.1.2  christos 	    "Failed to initialize decay");
     17  1.1.1.2  christos 	expect_zd_eq(decay_ms_read(&decay), decay_ms,
     18  1.1.1.2  christos 	    "Decay_ms was initialized incorrectly");
     19  1.1.1.2  christos 	expect_u64_ne(decay_epoch_duration_ns(&decay), 0,
     20  1.1.1.2  christos 	    "Epoch duration was initialized incorrectly");
     21      1.1  christos }
     22  1.1.1.2  christos TEST_END
     23      1.1  christos 
     24  1.1.1.2  christos TEST_BEGIN(test_decay_ms_valid) {
     25  1.1.1.2  christos 	expect_false(decay_ms_valid(-7),
     26  1.1.1.2  christos 	    "Misclassified negative decay as valid");
     27  1.1.1.2  christos 	expect_true(decay_ms_valid(-1),
     28  1.1.1.2  christos 	    "Misclassified -1 (never decay) as invalid decay");
     29  1.1.1.2  christos 	expect_true(decay_ms_valid(8943),
     30  1.1.1.2  christos 	    "Misclassified valid decay");
     31  1.1.1.2  christos 	if (SSIZE_MAX > NSTIME_SEC_MAX) {
     32  1.1.1.2  christos 		expect_false(
     33  1.1.1.2  christos 		    decay_ms_valid((ssize_t)(NSTIME_SEC_MAX * KQU(1000) + 39)),
     34  1.1.1.2  christos 		    "Misclassified too large decay");
     35      1.1  christos 	}
     36      1.1  christos }
     37  1.1.1.2  christos TEST_END
     38      1.1  christos 
     39  1.1.1.2  christos TEST_BEGIN(test_decay_npages_purge_in) {
     40  1.1.1.2  christos 	decay_t decay;
     41  1.1.1.2  christos 	memset(&decay, 0, sizeof(decay));
     42  1.1.1.2  christos 
     43  1.1.1.2  christos 	nstime_t curtime;
     44  1.1.1.2  christos 	nstime_init(&curtime, 0);
     45  1.1.1.2  christos 
     46  1.1.1.2  christos 	uint64_t decay_ms = 1000;
     47  1.1.1.2  christos 	nstime_t decay_nstime;
     48  1.1.1.2  christos 	nstime_init(&decay_nstime, decay_ms * 1000 * 1000);
     49  1.1.1.2  christos 	expect_false(decay_init(&decay, &curtime, (ssize_t)decay_ms),
     50  1.1.1.2  christos 	    "Failed to initialize decay");
     51  1.1.1.2  christos 
     52  1.1.1.2  christos 	size_t new_pages = 100;
     53  1.1.1.2  christos 
     54  1.1.1.2  christos 	nstime_t time;
     55  1.1.1.2  christos 	nstime_copy(&time, &decay_nstime);
     56  1.1.1.2  christos 	expect_u64_eq(decay_npages_purge_in(&decay, &time, new_pages),
     57  1.1.1.2  christos 	    new_pages, "Not all pages are expected to decay in decay_ms");
     58      1.1  christos 
     59  1.1.1.2  christos 	nstime_init(&time, 0);
     60  1.1.1.2  christos 	expect_u64_eq(decay_npages_purge_in(&decay, &time, new_pages), 0,
     61  1.1.1.2  christos 	    "More than zero pages are expected to instantly decay");
     62      1.1  christos 
     63  1.1.1.2  christos 	nstime_copy(&time, &decay_nstime);
     64  1.1.1.2  christos 	nstime_idivide(&time, 2);
     65  1.1.1.2  christos 	expect_u64_eq(decay_npages_purge_in(&decay, &time, new_pages),
     66  1.1.1.2  christos 	    new_pages / 2, "Not half of pages decay in half the decay period");
     67      1.1  christos }
     68  1.1.1.2  christos TEST_END
     69      1.1  christos 
     70  1.1.1.2  christos TEST_BEGIN(test_decay_maybe_advance_epoch) {
     71  1.1.1.2  christos 	decay_t decay;
     72  1.1.1.2  christos 	memset(&decay, 0, sizeof(decay));
     73      1.1  christos 
     74  1.1.1.2  christos 	nstime_t curtime;
     75  1.1.1.2  christos 	nstime_init(&curtime, 0);
     76      1.1  christos 
     77  1.1.1.2  christos 	uint64_t decay_ms = 1000;
     78      1.1  christos 
     79  1.1.1.2  christos 	bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
     80  1.1.1.2  christos 	expect_false(err, "");
     81      1.1  christos 
     82  1.1.1.2  christos 	bool advanced;
     83  1.1.1.2  christos 	advanced = decay_maybe_advance_epoch(&decay, &curtime, 0);
     84  1.1.1.2  christos 	expect_false(advanced, "Epoch advanced while time didn't");
     85      1.1  christos 
     86  1.1.1.2  christos 	nstime_t interval;
     87  1.1.1.2  christos 	nstime_init(&interval, decay_epoch_duration_ns(&decay));
     88      1.1  christos 
     89  1.1.1.2  christos 	nstime_add(&curtime, &interval);
     90  1.1.1.2  christos 	advanced = decay_maybe_advance_epoch(&decay, &curtime, 0);
     91  1.1.1.2  christos 	expect_false(advanced, "Epoch advanced after first interval");
     92      1.1  christos 
     93  1.1.1.2  christos 	nstime_add(&curtime, &interval);
     94  1.1.1.2  christos 	advanced = decay_maybe_advance_epoch(&decay, &curtime, 0);
     95  1.1.1.2  christos 	expect_true(advanced, "Epoch didn't advance after two intervals");
     96      1.1  christos }
     97      1.1  christos TEST_END
     98      1.1  christos 
     99  1.1.1.2  christos TEST_BEGIN(test_decay_empty) {
    100  1.1.1.2  christos 	/* If we never have any decaying pages, npages_limit should be 0. */
    101  1.1.1.2  christos 	decay_t decay;
    102  1.1.1.2  christos 	memset(&decay, 0, sizeof(decay));
    103  1.1.1.2  christos 
    104  1.1.1.2  christos 	nstime_t curtime;
    105  1.1.1.2  christos 	nstime_init(&curtime, 0);
    106  1.1.1.2  christos 
    107  1.1.1.2  christos 	uint64_t decay_ms = 1000;
    108  1.1.1.2  christos 	uint64_t decay_ns = decay_ms * 1000 * 1000;
    109  1.1.1.2  christos 
    110  1.1.1.2  christos 	bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
    111  1.1.1.2  christos 	assert_false(err, "");
    112  1.1.1.2  christos 
    113  1.1.1.2  christos 	uint64_t time_between_calls = decay_epoch_duration_ns(&decay) / 5;
    114  1.1.1.2  christos 	int nepochs = 0;
    115  1.1.1.2  christos 	for (uint64_t i = 0; i < decay_ns / time_between_calls * 10; i++) {
    116  1.1.1.2  christos 		size_t dirty_pages = 0;
    117  1.1.1.2  christos 		nstime_init(&curtime, i * time_between_calls);
    118  1.1.1.2  christos 		bool epoch_advanced = decay_maybe_advance_epoch(&decay,
    119  1.1.1.2  christos 		    &curtime, dirty_pages);
    120  1.1.1.2  christos 		if (epoch_advanced) {
    121  1.1.1.2  christos 			nepochs++;
    122  1.1.1.2  christos 			expect_zu_eq(decay_npages_limit_get(&decay), 0,
    123  1.1.1.2  christos 			    "Unexpectedly increased npages_limit");
    124      1.1  christos 		}
    125      1.1  christos 	}
    126  1.1.1.2  christos 	expect_d_gt(nepochs, 0, "Epochs never advanced");
    127      1.1  christos }
    128      1.1  christos TEST_END
    129      1.1  christos 
    130  1.1.1.2  christos /*
    131  1.1.1.2  christos  * Verify that npages_limit correctly decays as the time goes.
    132  1.1.1.2  christos  *
    133  1.1.1.2  christos  * During first 'nepoch_init' epochs, add new dirty pages.
    134  1.1.1.2  christos  * After that, let them decay and verify npages_limit decreases.
    135  1.1.1.2  christos  * Then proceed with another 'nepoch_init' epochs and check that
    136  1.1.1.2  christos  * all dirty pages are flushed out of backlog, bringing npages_limit
    137  1.1.1.2  christos  * down to zero.
    138  1.1.1.2  christos  */
    139  1.1.1.2  christos TEST_BEGIN(test_decay) {
    140  1.1.1.2  christos 	const uint64_t nepoch_init = 10;
    141  1.1.1.2  christos 
    142  1.1.1.2  christos 	decay_t decay;
    143  1.1.1.2  christos 	memset(&decay, 0, sizeof(decay));
    144  1.1.1.2  christos 
    145  1.1.1.2  christos 	nstime_t curtime;
    146  1.1.1.2  christos 	nstime_init(&curtime, 0);
    147  1.1.1.2  christos 
    148  1.1.1.2  christos 	uint64_t decay_ms = 1000;
    149  1.1.1.2  christos 	uint64_t decay_ns = decay_ms * 1000 * 1000;
    150  1.1.1.2  christos 
    151  1.1.1.2  christos 	bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
    152  1.1.1.2  christos 	assert_false(err, "");
    153  1.1.1.2  christos 
    154  1.1.1.2  christos 	expect_zu_eq(decay_npages_limit_get(&decay), 0,
    155  1.1.1.2  christos 	    "Empty decay returned nonzero npages_limit");
    156  1.1.1.2  christos 
    157  1.1.1.2  christos 	nstime_t epochtime;
    158  1.1.1.2  christos 	nstime_init(&epochtime, decay_epoch_duration_ns(&decay));
    159  1.1.1.2  christos 
    160  1.1.1.2  christos 	const size_t dirty_pages_per_epoch = 1000;
    161  1.1.1.2  christos 	size_t dirty_pages = 0;
    162  1.1.1.2  christos 	uint64_t epoch_ns = decay_epoch_duration_ns(&decay);
    163  1.1.1.2  christos 	bool epoch_advanced = false;
    164  1.1.1.2  christos 
    165  1.1.1.2  christos 	/* Populate backlog with some dirty pages */
    166  1.1.1.2  christos 	for (uint64_t i = 0; i < nepoch_init; i++) {
    167  1.1.1.2  christos 		nstime_add(&curtime, &epochtime);
    168  1.1.1.2  christos 		dirty_pages += dirty_pages_per_epoch;
    169  1.1.1.2  christos 		epoch_advanced |= decay_maybe_advance_epoch(&decay, &curtime,
    170  1.1.1.2  christos 		    dirty_pages);
    171  1.1.1.2  christos 	}
    172  1.1.1.2  christos 	expect_true(epoch_advanced, "Epoch never advanced");
    173  1.1.1.2  christos 
    174  1.1.1.2  christos 	size_t npages_limit = decay_npages_limit_get(&decay);
    175  1.1.1.2  christos 	expect_zu_gt(npages_limit, 0, "npages_limit is incorrectly equal "
    176  1.1.1.2  christos 	    "to zero after dirty pages have been added");
    177  1.1.1.2  christos 
    178  1.1.1.2  christos 	/* Keep dirty pages unchanged and verify that npages_limit decreases */
    179  1.1.1.2  christos 	for (uint64_t i = nepoch_init; i * epoch_ns < decay_ns; ++i) {
    180  1.1.1.2  christos 		nstime_add(&curtime, &epochtime);
    181  1.1.1.2  christos 		epoch_advanced = decay_maybe_advance_epoch(&decay, &curtime,
    182  1.1.1.2  christos 				    dirty_pages);
    183  1.1.1.2  christos 		if (epoch_advanced) {
    184  1.1.1.2  christos 			size_t npages_limit_new = decay_npages_limit_get(&decay);
    185  1.1.1.2  christos 			expect_zu_lt(npages_limit_new, npages_limit,
    186  1.1.1.2  christos 			    "napges_limit failed to decay");
    187      1.1  christos 
    188  1.1.1.2  christos 			npages_limit = npages_limit_new;
    189  1.1.1.2  christos 		}
    190      1.1  christos 	}
    191      1.1  christos 
    192  1.1.1.2  christos 	expect_zu_gt(npages_limit, 0, "npages_limit decayed to zero earlier "
    193  1.1.1.2  christos 	    "than decay_ms since last dirty page was added");
    194      1.1  christos 
    195  1.1.1.2  christos 	/* Completely push all dirty pages out of the backlog */
    196  1.1.1.2  christos 	epoch_advanced = false;
    197  1.1.1.2  christos 	for (uint64_t i = 0; i < nepoch_init; i++) {
    198  1.1.1.2  christos 		nstime_add(&curtime, &epochtime);
    199  1.1.1.2  christos 		epoch_advanced |= decay_maybe_advance_epoch(&decay, &curtime,
    200  1.1.1.2  christos 		    dirty_pages);
    201  1.1.1.2  christos 	}
    202  1.1.1.2  christos 	expect_true(epoch_advanced, "Epoch never advanced");
    203  1.1.1.2  christos 
    204  1.1.1.2  christos 	npages_limit = decay_npages_limit_get(&decay);
    205  1.1.1.2  christos 	expect_zu_eq(npages_limit, 0, "npages_limit didn't decay to 0 after "
    206  1.1.1.2  christos 	    "decay_ms since last bump in dirty pages");
    207      1.1  christos }
    208      1.1  christos TEST_END
    209      1.1  christos 
    210  1.1.1.2  christos TEST_BEGIN(test_decay_ns_until_purge) {
    211  1.1.1.2  christos 	const uint64_t nepoch_init = 10;
    212      1.1  christos 
    213  1.1.1.2  christos 	decay_t decay;
    214  1.1.1.2  christos 	memset(&decay, 0, sizeof(decay));
    215      1.1  christos 
    216  1.1.1.2  christos 	nstime_t curtime;
    217  1.1.1.2  christos 	nstime_init(&curtime, 0);
    218      1.1  christos 
    219  1.1.1.2  christos 	uint64_t decay_ms = 1000;
    220  1.1.1.2  christos 	uint64_t decay_ns = decay_ms * 1000 * 1000;
    221  1.1.1.2  christos 
    222  1.1.1.2  christos 	bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
    223  1.1.1.2  christos 	assert_false(err, "");
    224  1.1.1.2  christos 
    225  1.1.1.2  christos 	nstime_t epochtime;
    226  1.1.1.2  christos 	nstime_init(&epochtime, decay_epoch_duration_ns(&decay));
    227  1.1.1.2  christos 
    228  1.1.1.2  christos 	uint64_t ns_until_purge_empty = decay_ns_until_purge(&decay, 0, 0);
    229  1.1.1.2  christos 	expect_u64_eq(ns_until_purge_empty, DECAY_UNBOUNDED_TIME_TO_PURGE,
    230  1.1.1.2  christos 	    "Failed to return unbounded wait time for zero threshold");
    231  1.1.1.2  christos 
    232  1.1.1.2  christos 	const size_t dirty_pages_per_epoch = 1000;
    233  1.1.1.2  christos 	size_t dirty_pages = 0;
    234  1.1.1.2  christos 	bool epoch_advanced = false;
    235  1.1.1.2  christos 	for (uint64_t i = 0; i < nepoch_init; i++) {
    236  1.1.1.2  christos 		nstime_add(&curtime, &epochtime);
    237  1.1.1.2  christos 		dirty_pages += dirty_pages_per_epoch;
    238  1.1.1.2  christos 		epoch_advanced |= decay_maybe_advance_epoch(&decay, &curtime,
    239  1.1.1.2  christos 		    dirty_pages);
    240  1.1.1.2  christos 	}
    241  1.1.1.2  christos 	expect_true(epoch_advanced, "Epoch never advanced");
    242  1.1.1.2  christos 
    243  1.1.1.2  christos 	uint64_t ns_until_purge_all = decay_ns_until_purge(&decay,
    244  1.1.1.2  christos 	    dirty_pages, dirty_pages);
    245  1.1.1.2  christos 	expect_u64_ge(ns_until_purge_all, decay_ns,
    246  1.1.1.2  christos 	    "Incorrectly calculated time to purge all pages");
    247  1.1.1.2  christos 
    248  1.1.1.2  christos 	uint64_t ns_until_purge_none = decay_ns_until_purge(&decay,
    249  1.1.1.2  christos 	    dirty_pages, 0);
    250  1.1.1.2  christos 	expect_u64_eq(ns_until_purge_none, decay_epoch_duration_ns(&decay) * 2,
    251  1.1.1.2  christos 	    "Incorrectly calculated time to purge 0 pages");
    252  1.1.1.2  christos 
    253  1.1.1.2  christos 	uint64_t npages_threshold = dirty_pages / 2;
    254  1.1.1.2  christos 	uint64_t ns_until_purge_half = decay_ns_until_purge(&decay,
    255  1.1.1.2  christos 	    dirty_pages, npages_threshold);
    256  1.1.1.2  christos 
    257  1.1.1.2  christos 	nstime_t waittime;
    258  1.1.1.2  christos 	nstime_init(&waittime, ns_until_purge_half);
    259  1.1.1.2  christos 	nstime_add(&curtime, &waittime);
    260  1.1.1.2  christos 
    261  1.1.1.2  christos 	decay_maybe_advance_epoch(&decay, &curtime, dirty_pages);
    262  1.1.1.2  christos 	size_t npages_limit = decay_npages_limit_get(&decay);
    263  1.1.1.2  christos 	expect_zu_lt(npages_limit, dirty_pages,
    264  1.1.1.2  christos 	    "npages_limit failed to decrease after waiting");
    265  1.1.1.2  christos 	size_t expected = dirty_pages - npages_limit;
    266  1.1.1.2  christos 	int deviation = abs((int)expected - (int)(npages_threshold));
    267  1.1.1.2  christos 	expect_d_lt(deviation, (int)(npages_threshold / 2),
    268  1.1.1.2  christos 	    "After waiting, number of pages is out of the expected interval "
    269  1.1.1.2  christos 	    "[0.5 * npages_threshold .. 1.5 * npages_threshold]");
    270      1.1  christos }
    271      1.1  christos TEST_END
    272      1.1  christos 
    273      1.1  christos int
    274      1.1  christos main(void) {
    275      1.1  christos 	return test(
    276  1.1.1.2  christos 	    test_decay_init,
    277  1.1.1.2  christos 	    test_decay_ms_valid,
    278  1.1.1.2  christos 	    test_decay_npages_purge_in,
    279  1.1.1.2  christos 	    test_decay_maybe_advance_epoch,
    280  1.1.1.2  christos 	    test_decay_empty,
    281  1.1.1.2  christos 	    test_decay,
    282  1.1.1.2  christos 	    test_decay_ns_until_purge);
    283      1.1  christos }
    284