decay.c 8.4 KB

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