psset.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. #include "jemalloc/internal/jemalloc_preamble.h"
  2. #include "jemalloc/internal/jemalloc_internal_includes.h"
  3. #include "jemalloc/internal/psset.h"
  4. #include "jemalloc/internal/fb.h"
  5. void
  6. psset_init(psset_t *psset) {
  7. for (unsigned i = 0; i < PSSET_NPSIZES; i++) {
  8. hpdata_age_heap_new(&psset->pageslabs[i]);
  9. }
  10. fb_init(psset->pageslab_bitmap, PSSET_NPSIZES);
  11. memset(&psset->merged_stats, 0, sizeof(psset->merged_stats));
  12. memset(&psset->stats, 0, sizeof(psset->stats));
  13. hpdata_empty_list_init(&psset->empty);
  14. for (int i = 0; i < PSSET_NPURGE_LISTS; i++) {
  15. hpdata_purge_list_init(&psset->to_purge[i]);
  16. }
  17. fb_init(psset->purge_bitmap, PSSET_NPURGE_LISTS);
  18. hpdata_hugify_list_init(&psset->to_hugify);
  19. }
  20. static void
  21. psset_bin_stats_accum(psset_bin_stats_t *dst, psset_bin_stats_t *src) {
  22. dst->npageslabs += src->npageslabs;
  23. dst->nactive += src->nactive;
  24. dst->ndirty += src->ndirty;
  25. }
  26. void
  27. psset_stats_accum(psset_stats_t *dst, psset_stats_t *src) {
  28. psset_bin_stats_accum(&dst->full_slabs[0], &src->full_slabs[0]);
  29. psset_bin_stats_accum(&dst->full_slabs[1], &src->full_slabs[1]);
  30. psset_bin_stats_accum(&dst->empty_slabs[0], &src->empty_slabs[0]);
  31. psset_bin_stats_accum(&dst->empty_slabs[1], &src->empty_slabs[1]);
  32. for (pszind_t i = 0; i < PSSET_NPSIZES; i++) {
  33. psset_bin_stats_accum(&dst->nonfull_slabs[i][0],
  34. &src->nonfull_slabs[i][0]);
  35. psset_bin_stats_accum(&dst->nonfull_slabs[i][1],
  36. &src->nonfull_slabs[i][1]);
  37. }
  38. }
  39. /*
  40. * The stats maintenance strategy is to remove a pageslab's contribution to the
  41. * stats when we call psset_update_begin, and re-add it (to a potentially new
  42. * bin) when we call psset_update_end.
  43. */
  44. JEMALLOC_ALWAYS_INLINE void
  45. psset_bin_stats_insert_remove(psset_t *psset, psset_bin_stats_t *binstats,
  46. hpdata_t *ps, bool insert) {
  47. size_t mul = insert ? (size_t)1 : (size_t)-1;
  48. size_t huge_idx = (size_t)hpdata_huge_get(ps);
  49. binstats[huge_idx].npageslabs += mul * 1;
  50. binstats[huge_idx].nactive += mul * hpdata_nactive_get(ps);
  51. binstats[huge_idx].ndirty += mul * hpdata_ndirty_get(ps);
  52. psset->merged_stats.npageslabs += mul * 1;
  53. psset->merged_stats.nactive += mul * hpdata_nactive_get(ps);
  54. psset->merged_stats.ndirty += mul * hpdata_ndirty_get(ps);
  55. if (config_debug) {
  56. psset_bin_stats_t check_stats = {0};
  57. for (size_t huge = 0; huge <= 1; huge++) {
  58. psset_bin_stats_accum(&check_stats,
  59. &psset->stats.full_slabs[huge]);
  60. psset_bin_stats_accum(&check_stats,
  61. &psset->stats.empty_slabs[huge]);
  62. for (pszind_t pind = 0; pind < PSSET_NPSIZES; pind++) {
  63. psset_bin_stats_accum(&check_stats,
  64. &psset->stats.nonfull_slabs[pind][huge]);
  65. }
  66. }
  67. assert(psset->merged_stats.npageslabs
  68. == check_stats.npageslabs);
  69. assert(psset->merged_stats.nactive == check_stats.nactive);
  70. assert(psset->merged_stats.ndirty == check_stats.ndirty);
  71. }
  72. }
  73. static void
  74. psset_bin_stats_insert(psset_t *psset, psset_bin_stats_t *binstats,
  75. hpdata_t *ps) {
  76. psset_bin_stats_insert_remove(psset, binstats, ps, true);
  77. }
  78. static void
  79. psset_bin_stats_remove(psset_t *psset, psset_bin_stats_t *binstats,
  80. hpdata_t *ps) {
  81. psset_bin_stats_insert_remove(psset, binstats, ps, false);
  82. }
  83. static void
  84. psset_hpdata_heap_remove(psset_t *psset, pszind_t pind, hpdata_t *ps) {
  85. hpdata_age_heap_remove(&psset->pageslabs[pind], ps);
  86. if (hpdata_age_heap_empty(&psset->pageslabs[pind])) {
  87. fb_unset(psset->pageslab_bitmap, PSSET_NPSIZES, (size_t)pind);
  88. }
  89. }
  90. static void
  91. psset_hpdata_heap_insert(psset_t *psset, pszind_t pind, hpdata_t *ps) {
  92. if (hpdata_age_heap_empty(&psset->pageslabs[pind])) {
  93. fb_set(psset->pageslab_bitmap, PSSET_NPSIZES, (size_t)pind);
  94. }
  95. hpdata_age_heap_insert(&psset->pageslabs[pind], ps);
  96. }
  97. static void
  98. psset_stats_insert(psset_t* psset, hpdata_t *ps) {
  99. if (hpdata_empty(ps)) {
  100. psset_bin_stats_insert(psset, psset->stats.empty_slabs, ps);
  101. } else if (hpdata_full(ps)) {
  102. psset_bin_stats_insert(psset, psset->stats.full_slabs, ps);
  103. } else {
  104. size_t longest_free_range = hpdata_longest_free_range_get(ps);
  105. pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
  106. longest_free_range << LG_PAGE));
  107. assert(pind < PSSET_NPSIZES);
  108. psset_bin_stats_insert(psset, psset->stats.nonfull_slabs[pind],
  109. ps);
  110. }
  111. }
  112. static void
  113. psset_stats_remove(psset_t *psset, hpdata_t *ps) {
  114. if (hpdata_empty(ps)) {
  115. psset_bin_stats_remove(psset, psset->stats.empty_slabs, ps);
  116. } else if (hpdata_full(ps)) {
  117. psset_bin_stats_remove(psset, psset->stats.full_slabs, ps);
  118. } else {
  119. size_t longest_free_range = hpdata_longest_free_range_get(ps);
  120. pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
  121. longest_free_range << LG_PAGE));
  122. assert(pind < PSSET_NPSIZES);
  123. psset_bin_stats_remove(psset, psset->stats.nonfull_slabs[pind],
  124. ps);
  125. }
  126. }
  127. /*
  128. * Put ps into some container so that it can be found during future allocation
  129. * requests.
  130. */
  131. static void
  132. psset_alloc_container_insert(psset_t *psset, hpdata_t *ps) {
  133. assert(!hpdata_in_psset_alloc_container_get(ps));
  134. hpdata_in_psset_alloc_container_set(ps, true);
  135. if (hpdata_empty(ps)) {
  136. /*
  137. * This prepend, paired with popping the head in psset_fit,
  138. * means we implement LIFO ordering for the empty slabs set,
  139. * which seems reasonable.
  140. */
  141. hpdata_empty_list_prepend(&psset->empty, ps);
  142. } else if (hpdata_full(ps)) {
  143. /*
  144. * We don't need to keep track of the full slabs; we're never
  145. * going to return them from a psset_pick_alloc call.
  146. */
  147. } else {
  148. size_t longest_free_range = hpdata_longest_free_range_get(ps);
  149. pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
  150. longest_free_range << LG_PAGE));
  151. assert(pind < PSSET_NPSIZES);
  152. psset_hpdata_heap_insert(psset, pind, ps);
  153. }
  154. }
  155. /* Remove ps from those collections. */
  156. static void
  157. psset_alloc_container_remove(psset_t *psset, hpdata_t *ps) {
  158. assert(hpdata_in_psset_alloc_container_get(ps));
  159. hpdata_in_psset_alloc_container_set(ps, false);
  160. if (hpdata_empty(ps)) {
  161. hpdata_empty_list_remove(&psset->empty, ps);
  162. } else if (hpdata_full(ps)) {
  163. /* Same as above -- do nothing in this case. */
  164. } else {
  165. size_t longest_free_range = hpdata_longest_free_range_get(ps);
  166. pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
  167. longest_free_range << LG_PAGE));
  168. assert(pind < PSSET_NPSIZES);
  169. psset_hpdata_heap_remove(psset, pind, ps);
  170. }
  171. }
  172. static size_t
  173. psset_purge_list_ind(hpdata_t *ps) {
  174. size_t ndirty = hpdata_ndirty_get(ps);
  175. /* Shouldn't have something with no dirty pages purgeable. */
  176. assert(ndirty > 0);
  177. /*
  178. * Higher indices correspond to lists we'd like to purge earlier; make
  179. * the two highest indices correspond to empty lists, which we attempt
  180. * to purge before purging any non-empty list. This has two advantages:
  181. * - Empty page slabs are the least likely to get reused (we'll only
  182. * pick them for an allocation if we have no other choice).
  183. * - Empty page slabs can purge every dirty page they contain in a
  184. * single call, which is not usually the case.
  185. *
  186. * We purge hugeified empty slabs before nonhugeified ones, on the basis
  187. * that they are fully dirty, while nonhugified slabs might not be, so
  188. * we free up more pages more easily.
  189. */
  190. if (hpdata_nactive_get(ps) == 0) {
  191. if (hpdata_huge_get(ps)) {
  192. return PSSET_NPURGE_LISTS - 1;
  193. } else {
  194. return PSSET_NPURGE_LISTS - 2;
  195. }
  196. }
  197. pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(ndirty << LG_PAGE));
  198. /*
  199. * For non-empty slabs, we may reuse them again. Prefer purging
  200. * non-hugeified slabs before hugeified ones then, among pages of
  201. * similar dirtiness. We still get some benefit from the hugification.
  202. */
  203. return (size_t)pind * 2 + (hpdata_huge_get(ps) ? 0 : 1);
  204. }
  205. static void
  206. psset_maybe_remove_purge_list(psset_t *psset, hpdata_t *ps) {
  207. /*
  208. * Remove the hpdata from its purge list (if it's in one). Even if it's
  209. * going to stay in the same one, by appending it during
  210. * psset_update_end, we move it to the end of its queue, so that we
  211. * purge LRU within a given dirtiness bucket.
  212. */
  213. if (hpdata_purge_allowed_get(ps)) {
  214. size_t ind = psset_purge_list_ind(ps);
  215. hpdata_purge_list_t *purge_list = &psset->to_purge[ind];
  216. hpdata_purge_list_remove(purge_list, ps);
  217. if (hpdata_purge_list_empty(purge_list)) {
  218. fb_unset(psset->purge_bitmap, PSSET_NPURGE_LISTS, ind);
  219. }
  220. }
  221. }
  222. static void
  223. psset_maybe_insert_purge_list(psset_t *psset, hpdata_t *ps) {
  224. if (hpdata_purge_allowed_get(ps)) {
  225. size_t ind = psset_purge_list_ind(ps);
  226. hpdata_purge_list_t *purge_list = &psset->to_purge[ind];
  227. if (hpdata_purge_list_empty(purge_list)) {
  228. fb_set(psset->purge_bitmap, PSSET_NPURGE_LISTS, ind);
  229. }
  230. hpdata_purge_list_append(purge_list, ps);
  231. }
  232. }
  233. void
  234. psset_update_begin(psset_t *psset, hpdata_t *ps) {
  235. hpdata_assert_consistent(ps);
  236. assert(hpdata_in_psset_get(ps));
  237. hpdata_updating_set(ps, true);
  238. psset_stats_remove(psset, ps);
  239. if (hpdata_in_psset_alloc_container_get(ps)) {
  240. /*
  241. * Some metadata updates can break alloc container invariants
  242. * (e.g. the longest free range determines the hpdata_heap_t the
  243. * pageslab lives in).
  244. */
  245. assert(hpdata_alloc_allowed_get(ps));
  246. psset_alloc_container_remove(psset, ps);
  247. }
  248. psset_maybe_remove_purge_list(psset, ps);
  249. /*
  250. * We don't update presence in the hugify list; we try to keep it FIFO,
  251. * even in the presence of other metadata updates. We'll update
  252. * presence at the end of the metadata update if necessary.
  253. */
  254. }
  255. void
  256. psset_update_end(psset_t *psset, hpdata_t *ps) {
  257. assert(hpdata_in_psset_get(ps));
  258. hpdata_updating_set(ps, false);
  259. psset_stats_insert(psset, ps);
  260. /*
  261. * The update begin should have removed ps from whatever alloc container
  262. * it was in.
  263. */
  264. assert(!hpdata_in_psset_alloc_container_get(ps));
  265. if (hpdata_alloc_allowed_get(ps)) {
  266. psset_alloc_container_insert(psset, ps);
  267. }
  268. psset_maybe_insert_purge_list(psset, ps);
  269. if (hpdata_hugify_allowed_get(ps)
  270. && !hpdata_in_psset_hugify_container_get(ps)) {
  271. hpdata_in_psset_hugify_container_set(ps, true);
  272. hpdata_hugify_list_append(&psset->to_hugify, ps);
  273. } else if (!hpdata_hugify_allowed_get(ps)
  274. && hpdata_in_psset_hugify_container_get(ps)) {
  275. hpdata_in_psset_hugify_container_set(ps, false);
  276. hpdata_hugify_list_remove(&psset->to_hugify, ps);
  277. }
  278. hpdata_assert_consistent(ps);
  279. }
  280. hpdata_t *
  281. psset_pick_alloc(psset_t *psset, size_t size) {
  282. assert((size & PAGE_MASK) == 0);
  283. assert(size <= HUGEPAGE);
  284. pszind_t min_pind = sz_psz2ind(sz_psz_quantize_ceil(size));
  285. pszind_t pind = (pszind_t)fb_ffs(psset->pageslab_bitmap, PSSET_NPSIZES,
  286. (size_t)min_pind);
  287. if (pind == PSSET_NPSIZES) {
  288. return hpdata_empty_list_first(&psset->empty);
  289. }
  290. hpdata_t *ps = hpdata_age_heap_first(&psset->pageslabs[pind]);
  291. if (ps == NULL) {
  292. return NULL;
  293. }
  294. hpdata_assert_consistent(ps);
  295. return ps;
  296. }
  297. hpdata_t *
  298. psset_pick_purge(psset_t *psset) {
  299. ssize_t ind_ssz = fb_fls(psset->purge_bitmap, PSSET_NPURGE_LISTS,
  300. PSSET_NPURGE_LISTS - 1);
  301. if (ind_ssz < 0) {
  302. return NULL;
  303. }
  304. pszind_t ind = (pszind_t)ind_ssz;
  305. assert(ind < PSSET_NPURGE_LISTS);
  306. hpdata_t *ps = hpdata_purge_list_first(&psset->to_purge[ind]);
  307. assert(ps != NULL);
  308. return ps;
  309. }
  310. hpdata_t *
  311. psset_pick_hugify(psset_t *psset) {
  312. return hpdata_hugify_list_first(&psset->to_hugify);
  313. }
  314. void
  315. psset_insert(psset_t *psset, hpdata_t *ps) {
  316. hpdata_in_psset_set(ps, true);
  317. psset_stats_insert(psset, ps);
  318. if (hpdata_alloc_allowed_get(ps)) {
  319. psset_alloc_container_insert(psset, ps);
  320. }
  321. psset_maybe_insert_purge_list(psset, ps);
  322. if (hpdata_hugify_allowed_get(ps)) {
  323. hpdata_in_psset_hugify_container_set(ps, true);
  324. hpdata_hugify_list_append(&psset->to_hugify, ps);
  325. }
  326. }
  327. void
  328. psset_remove(psset_t *psset, hpdata_t *ps) {
  329. hpdata_in_psset_set(ps, false);
  330. psset_stats_remove(psset, ps);
  331. if (hpdata_in_psset_alloc_container_get(ps)) {
  332. psset_alloc_container_remove(psset, ps);
  333. }
  334. psset_maybe_remove_purge_list(psset, ps);
  335. if (hpdata_in_psset_hugify_container_get(ps)) {
  336. hpdata_in_psset_hugify_container_set(ps, false);
  337. hpdata_hugify_list_remove(&psset->to_hugify, ps);
  338. }
  339. }