cache_bin.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. #include "test/jemalloc_test.h"
  2. static void
  3. do_fill_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
  4. cache_bin_sz_t ncached_max, cache_bin_sz_t nfill_attempt,
  5. cache_bin_sz_t nfill_succeed) {
  6. bool success;
  7. void *ptr;
  8. assert_true(cache_bin_ncached_get_local(bin, info) == 0, "");
  9. CACHE_BIN_PTR_ARRAY_DECLARE(arr, nfill_attempt);
  10. cache_bin_init_ptr_array_for_fill(bin, info, &arr, nfill_attempt);
  11. for (cache_bin_sz_t i = 0; i < nfill_succeed; i++) {
  12. arr.ptr[i] = &ptrs[i];
  13. }
  14. cache_bin_finish_fill(bin, info, &arr, nfill_succeed);
  15. expect_true(cache_bin_ncached_get_local(bin, info) == nfill_succeed,
  16. "");
  17. cache_bin_low_water_set(bin);
  18. for (cache_bin_sz_t i = 0; i < nfill_succeed; i++) {
  19. ptr = cache_bin_alloc(bin, &success);
  20. expect_true(success, "");
  21. expect_ptr_eq(ptr, (void *)&ptrs[i],
  22. "Should pop in order filled");
  23. expect_true(cache_bin_low_water_get(bin, info)
  24. == nfill_succeed - i - 1, "");
  25. }
  26. expect_true(cache_bin_ncached_get_local(bin, info) == 0, "");
  27. expect_true(cache_bin_low_water_get(bin, info) == 0, "");
  28. }
  29. static void
  30. do_flush_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
  31. cache_bin_sz_t nfill, cache_bin_sz_t nflush) {
  32. bool success;
  33. assert_true(cache_bin_ncached_get_local(bin, info) == 0, "");
  34. for (cache_bin_sz_t i = 0; i < nfill; i++) {
  35. success = cache_bin_dalloc_easy(bin, &ptrs[i]);
  36. expect_true(success, "");
  37. }
  38. CACHE_BIN_PTR_ARRAY_DECLARE(arr, nflush);
  39. cache_bin_init_ptr_array_for_flush(bin, info, &arr, nflush);
  40. for (cache_bin_sz_t i = 0; i < nflush; i++) {
  41. expect_ptr_eq(arr.ptr[i], &ptrs[nflush - i - 1], "");
  42. }
  43. cache_bin_finish_flush(bin, info, &arr, nflush);
  44. expect_true(cache_bin_ncached_get_local(bin, info) == nfill - nflush,
  45. "");
  46. while (cache_bin_ncached_get_local(bin, info) > 0) {
  47. cache_bin_alloc(bin, &success);
  48. }
  49. }
  50. static void
  51. do_batch_alloc_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
  52. cache_bin_sz_t nfill, size_t batch) {
  53. assert_true(cache_bin_ncached_get_local(bin, info) == 0, "");
  54. CACHE_BIN_PTR_ARRAY_DECLARE(arr, nfill);
  55. cache_bin_init_ptr_array_for_fill(bin, info, &arr, nfill);
  56. for (cache_bin_sz_t i = 0; i < nfill; i++) {
  57. arr.ptr[i] = &ptrs[i];
  58. }
  59. cache_bin_finish_fill(bin, info, &arr, nfill);
  60. assert_true(cache_bin_ncached_get_local(bin, info) == nfill, "");
  61. cache_bin_low_water_set(bin);
  62. void **out = malloc((batch + 1) * sizeof(void *));
  63. size_t n = cache_bin_alloc_batch(bin, batch, out);
  64. assert_true(n == ((size_t)nfill < batch ? (size_t)nfill : batch), "");
  65. for (cache_bin_sz_t i = 0; i < (cache_bin_sz_t)n; i++) {
  66. expect_ptr_eq(out[i], &ptrs[i], "");
  67. }
  68. expect_true(cache_bin_low_water_get(bin, info) == nfill -
  69. (cache_bin_sz_t)n, "");
  70. while (cache_bin_ncached_get_local(bin, info) > 0) {
  71. bool success;
  72. cache_bin_alloc(bin, &success);
  73. }
  74. free(out);
  75. }
  76. static void
  77. test_bin_init(cache_bin_t *bin, cache_bin_info_t *info) {
  78. size_t size;
  79. size_t alignment;
  80. cache_bin_info_compute_alloc(info, 1, &size, &alignment);
  81. void *mem = mallocx(size, MALLOCX_ALIGN(alignment));
  82. assert_ptr_not_null(mem, "Unexpected mallocx failure");
  83. size_t cur_offset = 0;
  84. cache_bin_preincrement(info, 1, mem, &cur_offset);
  85. cache_bin_init(bin, info, mem, &cur_offset);
  86. cache_bin_postincrement(info, 1, mem, &cur_offset);
  87. assert_zu_eq(cur_offset, size, "Should use all requested memory");
  88. }
  89. TEST_BEGIN(test_cache_bin) {
  90. const int ncached_max = 100;
  91. bool success;
  92. void *ptr;
  93. cache_bin_info_t info;
  94. cache_bin_info_init(&info, ncached_max);
  95. cache_bin_t bin;
  96. test_bin_init(&bin, &info);
  97. /* Initialize to empty; should then have 0 elements. */
  98. expect_d_eq(ncached_max, cache_bin_info_ncached_max(&info), "");
  99. expect_true(cache_bin_ncached_get_local(&bin, &info) == 0, "");
  100. expect_true(cache_bin_low_water_get(&bin, &info) == 0, "");
  101. ptr = cache_bin_alloc_easy(&bin, &success);
  102. expect_false(success, "Shouldn't successfully allocate when empty");
  103. expect_ptr_null(ptr, "Shouldn't get a non-null pointer on failure");
  104. ptr = cache_bin_alloc(&bin, &success);
  105. expect_false(success, "Shouldn't successfully allocate when empty");
  106. expect_ptr_null(ptr, "Shouldn't get a non-null pointer on failure");
  107. /*
  108. * We allocate one more item than ncached_max, so we can test cache bin
  109. * exhaustion.
  110. */
  111. void **ptrs = mallocx(sizeof(void *) * (ncached_max + 1), 0);
  112. assert_ptr_not_null(ptrs, "Unexpected mallocx failure");
  113. for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
  114. expect_true(cache_bin_ncached_get_local(&bin, &info) == i, "");
  115. success = cache_bin_dalloc_easy(&bin, &ptrs[i]);
  116. expect_true(success,
  117. "Should be able to dalloc into a non-full cache bin.");
  118. expect_true(cache_bin_low_water_get(&bin, &info) == 0,
  119. "Pushes and pops shouldn't change low water of zero.");
  120. }
  121. expect_true(cache_bin_ncached_get_local(&bin, &info) == ncached_max,
  122. "");
  123. success = cache_bin_dalloc_easy(&bin, &ptrs[ncached_max]);
  124. expect_false(success, "Shouldn't be able to dalloc into a full bin.");
  125. cache_bin_low_water_set(&bin);
  126. for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
  127. expect_true(cache_bin_low_water_get(&bin, &info)
  128. == ncached_max - i, "");
  129. expect_true(cache_bin_ncached_get_local(&bin, &info)
  130. == ncached_max - i, "");
  131. /*
  132. * This should fail -- the easy variant can't change the low
  133. * water mark.
  134. */
  135. ptr = cache_bin_alloc_easy(&bin, &success);
  136. expect_ptr_null(ptr, "");
  137. expect_false(success, "");
  138. expect_true(cache_bin_low_water_get(&bin, &info)
  139. == ncached_max - i, "");
  140. expect_true(cache_bin_ncached_get_local(&bin, &info)
  141. == ncached_max - i, "");
  142. /* This should succeed, though. */
  143. ptr = cache_bin_alloc(&bin, &success);
  144. expect_true(success, "");
  145. expect_ptr_eq(ptr, &ptrs[ncached_max - i - 1],
  146. "Alloc should pop in stack order");
  147. expect_true(cache_bin_low_water_get(&bin, &info)
  148. == ncached_max - i - 1, "");
  149. expect_true(cache_bin_ncached_get_local(&bin, &info)
  150. == ncached_max - i - 1, "");
  151. }
  152. /* Now we're empty -- all alloc attempts should fail. */
  153. expect_true(cache_bin_ncached_get_local(&bin, &info) == 0, "");
  154. ptr = cache_bin_alloc_easy(&bin, &success);
  155. expect_ptr_null(ptr, "");
  156. expect_false(success, "");
  157. ptr = cache_bin_alloc(&bin, &success);
  158. expect_ptr_null(ptr, "");
  159. expect_false(success, "");
  160. for (cache_bin_sz_t i = 0; i < ncached_max / 2; i++) {
  161. cache_bin_dalloc_easy(&bin, &ptrs[i]);
  162. }
  163. cache_bin_low_water_set(&bin);
  164. for (cache_bin_sz_t i = ncached_max / 2; i < ncached_max; i++) {
  165. cache_bin_dalloc_easy(&bin, &ptrs[i]);
  166. }
  167. expect_true(cache_bin_ncached_get_local(&bin, &info) == ncached_max,
  168. "");
  169. for (cache_bin_sz_t i = ncached_max - 1; i >= ncached_max / 2; i--) {
  170. /*
  171. * Size is bigger than low water -- the reduced version should
  172. * succeed.
  173. */
  174. ptr = cache_bin_alloc_easy(&bin, &success);
  175. expect_true(success, "");
  176. expect_ptr_eq(ptr, &ptrs[i], "");
  177. }
  178. /* But now, we've hit low-water. */
  179. ptr = cache_bin_alloc_easy(&bin, &success);
  180. expect_false(success, "");
  181. expect_ptr_null(ptr, "");
  182. /* We're going to test filling -- we must be empty to start. */
  183. while (cache_bin_ncached_get_local(&bin, &info)) {
  184. cache_bin_alloc(&bin, &success);
  185. expect_true(success, "");
  186. }
  187. /* Test fill. */
  188. /* Try to fill all, succeed fully. */
  189. do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max, ncached_max);
  190. /* Try to fill all, succeed partially. */
  191. do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max,
  192. ncached_max / 2);
  193. /* Try to fill all, fail completely. */
  194. do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max, 0);
  195. /* Try to fill some, succeed fully. */
  196. do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max / 2,
  197. ncached_max / 2);
  198. /* Try to fill some, succeed partially. */
  199. do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max / 2,
  200. ncached_max / 4);
  201. /* Try to fill some, fail completely. */
  202. do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max / 2, 0);
  203. do_flush_test(&bin, &info, ptrs, ncached_max, ncached_max);
  204. do_flush_test(&bin, &info, ptrs, ncached_max, ncached_max / 2);
  205. do_flush_test(&bin, &info, ptrs, ncached_max, 0);
  206. do_flush_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 2);
  207. do_flush_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 4);
  208. do_flush_test(&bin, &info, ptrs, ncached_max / 2, 0);
  209. do_batch_alloc_test(&bin, &info, ptrs, ncached_max, ncached_max);
  210. do_batch_alloc_test(&bin, &info, ptrs, ncached_max, ncached_max * 2);
  211. do_batch_alloc_test(&bin, &info, ptrs, ncached_max, ncached_max / 2);
  212. do_batch_alloc_test(&bin, &info, ptrs, ncached_max, 2);
  213. do_batch_alloc_test(&bin, &info, ptrs, ncached_max, 1);
  214. do_batch_alloc_test(&bin, &info, ptrs, ncached_max, 0);
  215. do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2,
  216. ncached_max / 2);
  217. do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, ncached_max);
  218. do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2,
  219. ncached_max / 4);
  220. do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, 2);
  221. do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, 1);
  222. do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, 0);
  223. do_batch_alloc_test(&bin, &info, ptrs, 2, ncached_max);
  224. do_batch_alloc_test(&bin, &info, ptrs, 2, 2);
  225. do_batch_alloc_test(&bin, &info, ptrs, 2, 1);
  226. do_batch_alloc_test(&bin, &info, ptrs, 2, 0);
  227. do_batch_alloc_test(&bin, &info, ptrs, 1, 2);
  228. do_batch_alloc_test(&bin, &info, ptrs, 1, 1);
  229. do_batch_alloc_test(&bin, &info, ptrs, 1, 0);
  230. do_batch_alloc_test(&bin, &info, ptrs, 0, 2);
  231. do_batch_alloc_test(&bin, &info, ptrs, 0, 1);
  232. do_batch_alloc_test(&bin, &info, ptrs, 0, 0);
  233. free(ptrs);
  234. }
  235. TEST_END
  236. static void
  237. do_flush_stashed_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
  238. cache_bin_sz_t nfill, cache_bin_sz_t nstash) {
  239. expect_true(cache_bin_ncached_get_local(bin, info) == 0,
  240. "Bin not empty");
  241. expect_true(cache_bin_nstashed_get_local(bin, info) == 0,
  242. "Bin not empty");
  243. expect_true(nfill + nstash <= info->ncached_max, "Exceeded max");
  244. bool ret;
  245. /* Fill */
  246. for (cache_bin_sz_t i = 0; i < nfill; i++) {
  247. ret = cache_bin_dalloc_easy(bin, &ptrs[i]);
  248. expect_true(ret, "Unexpected fill failure");
  249. }
  250. expect_true(cache_bin_ncached_get_local(bin, info) == nfill,
  251. "Wrong cached count");
  252. /* Stash */
  253. for (cache_bin_sz_t i = 0; i < nstash; i++) {
  254. ret = cache_bin_stash(bin, &ptrs[i + nfill]);
  255. expect_true(ret, "Unexpected stash failure");
  256. }
  257. expect_true(cache_bin_nstashed_get_local(bin, info) == nstash,
  258. "Wrong stashed count");
  259. if (nfill + nstash == info->ncached_max) {
  260. ret = cache_bin_dalloc_easy(bin, &ptrs[0]);
  261. expect_false(ret, "Should not dalloc into a full bin");
  262. ret = cache_bin_stash(bin, &ptrs[0]);
  263. expect_false(ret, "Should not stash into a full bin");
  264. }
  265. /* Alloc filled ones */
  266. for (cache_bin_sz_t i = 0; i < nfill; i++) {
  267. void *ptr = cache_bin_alloc(bin, &ret);
  268. expect_true(ret, "Unexpected alloc failure");
  269. /* Verify it's not from the stashed range. */
  270. expect_true((uintptr_t)ptr < (uintptr_t)&ptrs[nfill],
  271. "Should not alloc stashed ptrs");
  272. }
  273. expect_true(cache_bin_ncached_get_local(bin, info) == 0,
  274. "Wrong cached count");
  275. expect_true(cache_bin_nstashed_get_local(bin, info) == nstash,
  276. "Wrong stashed count");
  277. cache_bin_alloc(bin, &ret);
  278. expect_false(ret, "Should not alloc stashed");
  279. /* Clear stashed ones */
  280. cache_bin_finish_flush_stashed(bin, info);
  281. expect_true(cache_bin_ncached_get_local(bin, info) == 0,
  282. "Wrong cached count");
  283. expect_true(cache_bin_nstashed_get_local(bin, info) == 0,
  284. "Wrong stashed count");
  285. cache_bin_alloc(bin, &ret);
  286. expect_false(ret, "Should not alloc from empty bin");
  287. }
  288. TEST_BEGIN(test_cache_bin_stash) {
  289. const int ncached_max = 100;
  290. cache_bin_t bin;
  291. cache_bin_info_t info;
  292. cache_bin_info_init(&info, ncached_max);
  293. test_bin_init(&bin, &info);
  294. /*
  295. * The content of this array is not accessed; instead the interior
  296. * addresses are used to insert / stash into the bins as test pointers.
  297. */
  298. void **ptrs = mallocx(sizeof(void *) * (ncached_max + 1), 0);
  299. assert_ptr_not_null(ptrs, "Unexpected mallocx failure");
  300. bool ret;
  301. for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
  302. expect_true(cache_bin_ncached_get_local(&bin, &info) ==
  303. (i / 2 + i % 2), "Wrong ncached value");
  304. expect_true(cache_bin_nstashed_get_local(&bin, &info) == i / 2,
  305. "Wrong nstashed value");
  306. if (i % 2 == 0) {
  307. cache_bin_dalloc_easy(&bin, &ptrs[i]);
  308. } else {
  309. ret = cache_bin_stash(&bin, &ptrs[i]);
  310. expect_true(ret, "Should be able to stash into a "
  311. "non-full cache bin");
  312. }
  313. }
  314. ret = cache_bin_dalloc_easy(&bin, &ptrs[0]);
  315. expect_false(ret, "Should not dalloc into a full cache bin");
  316. ret = cache_bin_stash(&bin, &ptrs[0]);
  317. expect_false(ret, "Should not stash into a full cache bin");
  318. for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
  319. void *ptr = cache_bin_alloc(&bin, &ret);
  320. if (i < ncached_max / 2) {
  321. expect_true(ret, "Should be able to alloc");
  322. uintptr_t diff = ((uintptr_t)ptr - (uintptr_t)&ptrs[0])
  323. / sizeof(void *);
  324. expect_true(diff % 2 == 0, "Should be able to alloc");
  325. } else {
  326. expect_false(ret, "Should not alloc stashed");
  327. expect_true(cache_bin_nstashed_get_local(&bin, &info) ==
  328. ncached_max / 2, "Wrong nstashed value");
  329. }
  330. }
  331. test_bin_init(&bin, &info);
  332. do_flush_stashed_test(&bin, &info, ptrs, ncached_max, 0);
  333. do_flush_stashed_test(&bin, &info, ptrs, 0, ncached_max);
  334. do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 2);
  335. do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 4, ncached_max / 2);
  336. do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 4);
  337. do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 4, ncached_max / 4);
  338. }
  339. TEST_END
  340. int
  341. main(void) {
  342. return test(test_cache_bin,
  343. test_cache_bin_stash);
  344. }