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