JemallocHugePageAllocatorTest.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. /*
  2. * Copyright 2016-present Facebook, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <folly/experimental/JemallocHugePageAllocator.h>
  17. #include <folly/container/F14Map.h>
  18. #include <folly/memory/Malloc.h>
  19. #include <folly/portability/GTest.h>
  20. #include <vector>
  21. using jha = folly::JemallocHugePageAllocator;
  22. static constexpr int kb(int kilos) {
  23. return kilos * 1024;
  24. }
  25. static constexpr int mb(int megs) {
  26. return kb(megs * 1024);
  27. }
  28. TEST(JemallocHugePageAllocatorTest, Basic) {
  29. EXPECT_FALSE(jha::initialized());
  30. // Allocation should work even if uninitialized
  31. auto ptr = jha::allocate(kb(1));
  32. EXPECT_NE(nullptr, ptr);
  33. jha::deallocate(ptr);
  34. bool initialized = jha::init(1);
  35. if (initialized) {
  36. EXPECT_NE(0, jha::freeSpace());
  37. }
  38. ptr = jha::allocate(kb(1));
  39. EXPECT_NE(nullptr, ptr);
  40. if (initialized) {
  41. EXPECT_TRUE(jha::addressInArena(ptr));
  42. }
  43. // Allocate some arrays on huge page
  44. auto array_of_arrays = new (ptr) std::array<int, 100>[5];
  45. if (initialized) {
  46. EXPECT_FALSE(jha::addressInArena(&array_of_arrays));
  47. EXPECT_TRUE(jha::addressInArena(&array_of_arrays[0]));
  48. EXPECT_TRUE(jha::addressInArena(&array_of_arrays[0][0]));
  49. }
  50. jha::deallocate(ptr);
  51. }
  52. TEST(JemallocHugePageAllocatorTest, LargeAllocations) {
  53. // Allocate before init - will not use huge pages
  54. void* ptr0 = jha::allocate(kb(1));
  55. // One 2MB huge page
  56. bool initialized = jha::init(1);
  57. if (initialized) {
  58. EXPECT_NE(0, jha::freeSpace());
  59. }
  60. // This fits
  61. void* ptr1 = jha::allocate(mb(1) + kb(512));
  62. EXPECT_NE(nullptr, ptr1);
  63. if (initialized) {
  64. EXPECT_TRUE(jha::addressInArena(ptr1));
  65. }
  66. // This is too large to fit
  67. void* ptr2 = jha::allocate(mb(1));
  68. EXPECT_NE(nullptr, ptr2);
  69. EXPECT_FALSE(jha::addressInArena(ptr2));
  70. // Free and reuse huge page area
  71. jha::deallocate(ptr2);
  72. jha::deallocate(ptr0);
  73. ptr2 = jha::allocate(mb(1));
  74. // No memory in the huge page arena was freed - ptr0 was allocated
  75. // before init and ptr2 didn't fit
  76. EXPECT_FALSE(jha::addressInArena(ptr2));
  77. jha::deallocate(ptr1);
  78. void* ptr3 = jha::allocate(mb(1) + kb(512));
  79. EXPECT_NE(nullptr, ptr3);
  80. if (initialized) {
  81. EXPECT_EQ(ptr1, ptr3);
  82. EXPECT_TRUE(jha::addressInArena(ptr3));
  83. }
  84. // Just using free works equally well
  85. free(ptr3);
  86. ptr3 = jha::allocate(mb(1) + kb(512));
  87. EXPECT_NE(nullptr, ptr3);
  88. if (initialized) {
  89. EXPECT_TRUE(jha::addressInArena(ptr3));
  90. }
  91. jha::deallocate(ptr2);
  92. jha::deallocate(ptr3);
  93. }
  94. TEST(JemallocHugePageAllocatorTest, MemoryUsageTest) {
  95. bool initialized = jha::init(80);
  96. if (initialized) {
  97. EXPECT_GE(jha::freeSpace(), mb(160));
  98. }
  99. struct c32 {
  100. char val[32];
  101. };
  102. using Vec32 = std::vector<c32, folly::CxxHugePageAllocator<c32>>;
  103. Vec32 vec32;
  104. for (int i = 0; i < 10; i++) {
  105. vec32.push_back({});
  106. }
  107. void* ptr1 = jha::allocate(32);
  108. if (initialized) {
  109. EXPECT_GE(jha::freeSpace(), mb(158));
  110. }
  111. struct c320 {
  112. char val[320];
  113. };
  114. using Vec320 = std::vector<c320, folly::CxxHugePageAllocator<c320>>;
  115. Vec320 vec320;
  116. for (int i = 0; i < 10; i++) {
  117. vec320.push_back({});
  118. }
  119. void* ptr2 = jha::allocate(320);
  120. if (initialized) {
  121. EXPECT_GE(jha::freeSpace(), mb(158));
  122. }
  123. // Helper to ensure all allocations are freed at the end
  124. auto deleter = [](void* data) { jha::deallocate(data); };
  125. std::vector<std::unique_ptr<void, decltype(deleter)>> ptr_vec;
  126. auto alloc = [&ptr_vec, &deleter](size_t size) {
  127. ptr_vec.emplace_back(jha::allocate(size), deleter);
  128. };
  129. for (int i = 0; i < 10; i++) {
  130. alloc(kb(1));
  131. }
  132. void* ptr3 = jha::allocate(kb(1));
  133. if (initialized) {
  134. EXPECT_GE(jha::freeSpace(), mb(158));
  135. }
  136. for (int i = 0; i < 10; i++) {
  137. alloc(kb(4));
  138. }
  139. void* ptr4 = jha::allocate(kb(4));
  140. if (initialized) {
  141. EXPECT_GE(jha::freeSpace(), mb(158));
  142. }
  143. for (int i = 0; i < 10; i++) {
  144. alloc(kb(10));
  145. }
  146. void* ptr5 = jha::allocate(kb(10));
  147. if (initialized) {
  148. EXPECT_GE(jha::freeSpace(), mb(158));
  149. }
  150. alloc(kb(512));
  151. alloc(mb(1));
  152. void* ptr6 = jha::allocate(mb(1));
  153. if (initialized) {
  154. EXPECT_GE(jha::freeSpace(), mb(156));
  155. }
  156. alloc(mb(2));
  157. alloc(mb(4));
  158. void* ptr7 = jha::allocate(mb(4));
  159. if (initialized) {
  160. EXPECT_GE(jha::freeSpace(), mb(146));
  161. }
  162. alloc(kb(512));
  163. alloc(kb(512));
  164. if (initialized) {
  165. EXPECT_GE(jha::freeSpace(), 145);
  166. }
  167. void* ptr8 = jha::allocate(mb(64));
  168. if (initialized) {
  169. EXPECT_GE(jha::freeSpace(), mb(80));
  170. }
  171. alloc(mb(64));
  172. if (initialized) {
  173. EXPECT_GE(jha::freeSpace(), mb(16));
  174. }
  175. alloc(mb(256));
  176. alloc(mb(256));
  177. alloc(mb(256));
  178. // Now free a bunch of objects and then reallocate
  179. // the same size objects again.
  180. // This should not result in usage of free space.
  181. size_t free = jha::freeSpace();
  182. jha::deallocate(ptr1);
  183. jha::deallocate(ptr2);
  184. jha::deallocate(ptr3);
  185. jha::deallocate(ptr4);
  186. jha::deallocate(ptr5);
  187. jha::deallocate(ptr6);
  188. jha::deallocate(ptr7);
  189. jha::deallocate(ptr8);
  190. alloc(32);
  191. alloc(320);
  192. alloc(kb(1));
  193. alloc(kb(4));
  194. alloc(kb(10));
  195. alloc(mb(1));
  196. alloc(mb(4));
  197. alloc(mb(64));
  198. if (initialized) {
  199. EXPECT_EQ(free, jha::freeSpace());
  200. }
  201. }
  202. TEST(JemallocHugePageAllocatorTest, STLAllocator) {
  203. using MyVecAllocator = folly::CxxHugePageAllocator<int>;
  204. using MyVec = std::vector<int, MyVecAllocator>;
  205. using MyMapAllocator =
  206. folly::CxxHugePageAllocator<folly::f14::detail::MapValueType<int, MyVec>>;
  207. using MyMap = folly::F14FastMap<
  208. int,
  209. MyVec,
  210. folly::f14::DefaultHasher<int>,
  211. folly::f14::DefaultKeyEqual<int>,
  212. MyMapAllocator>;
  213. MyVec vec;
  214. // This should work, just won't get huge pages since
  215. // init hasn't been called yet
  216. vec.reserve(100);
  217. EXPECT_NE(nullptr, &vec[0]);
  218. // Reserve & initialize, not on huge pages
  219. MyVec vec2(100);
  220. EXPECT_NE(nullptr, &vec[0]);
  221. // F14 maps need quite a lot of memory by default
  222. bool initialized = jha::init(4);
  223. if (initialized) {
  224. EXPECT_NE(0, jha::freeSpace());
  225. }
  226. // Reallocate, this time on huge pages
  227. vec.reserve(200);
  228. EXPECT_NE(nullptr, &vec[0]);
  229. MyMap map1;
  230. map1[0] = {1, 2, 3};
  231. auto map2_ptr = std::make_unique<MyMap>();
  232. MyMap& map2 = *map2_ptr;
  233. map2[0] = {1, 2, 3};
  234. if (initialized) {
  235. EXPECT_TRUE(jha::addressInArena(&vec[0]));
  236. EXPECT_TRUE(jha::addressInArena(&map1[0]));
  237. EXPECT_TRUE(jha::addressInArena(&map1[0][0]));
  238. EXPECT_TRUE(jha::addressInArena(&map2[0]));
  239. EXPECT_TRUE(jha::addressInArena(&map2[0][0]));
  240. }
  241. // This will be on the huge page arena
  242. map1[0] = std::move(vec);
  243. // But not this, since vec2 content was allocated before init
  244. map1[1] = std::move(vec2);
  245. if (initialized) {
  246. EXPECT_TRUE(jha::addressInArena(&map1[0]));
  247. EXPECT_TRUE(jha::addressInArena(&map1[1]));
  248. EXPECT_TRUE(jha::addressInArena(&map1[0][0]));
  249. EXPECT_FALSE(jha::addressInArena(&map1[1][0]));
  250. }
  251. // realloc on huge pages
  252. map1[1].reserve(200);
  253. if (initialized) {
  254. EXPECT_TRUE(jha::addressInArena(&map1[1][0]));
  255. }
  256. }