pool_ut.cpp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. #include "pool.h"
  2. #include <library/cpp/testing/unittest/registar.h>
  3. #include <util/stream/output.h>
  4. class TCheckedAllocator: public TDefaultAllocator {
  5. public:
  6. inline TCheckedAllocator()
  7. : Alloced_(0)
  8. , Released_(0)
  9. , Allocs_(0)
  10. , Frees_(0)
  11. {
  12. }
  13. TBlock Allocate(size_t len) override {
  14. Check();
  15. Alloced_ += len;
  16. ++Allocs_;
  17. return TDefaultAllocator::Allocate(len);
  18. }
  19. void Release(const TBlock& block) override {
  20. Released_ += block.Len;
  21. ++Frees_;
  22. Check();
  23. TDefaultAllocator::Release(block);
  24. }
  25. inline void CheckAtEnd() {
  26. UNIT_ASSERT_EQUAL(Alloced_, Released_);
  27. UNIT_ASSERT_EQUAL(Allocs_, Frees_);
  28. }
  29. private:
  30. inline void Check() {
  31. UNIT_ASSERT(Alloced_ >= Released_);
  32. UNIT_ASSERT(Allocs_ >= Frees_);
  33. }
  34. private:
  35. size_t Alloced_;
  36. size_t Released_;
  37. size_t Allocs_;
  38. size_t Frees_;
  39. };
  40. class TErrorOnCopy {
  41. public:
  42. TErrorOnCopy() = default;
  43. TErrorOnCopy(TErrorOnCopy&&) = default;
  44. TErrorOnCopy(const TErrorOnCopy&) {
  45. UNIT_ASSERT(false);
  46. }
  47. };
  48. class TNoCopy {
  49. public:
  50. TNoCopy() = default;
  51. TNoCopy(TNoCopy&&) = default;
  52. TNoCopy(const TNoCopy&) = delete;
  53. };
  54. class TNoMove {
  55. public:
  56. TNoMove() = default;
  57. TNoMove(const TNoMove&) = default;
  58. TNoMove(TNoMove&&) = delete;
  59. };
  60. class TMemPoolTest: public TTestBase {
  61. UNIT_TEST_SUITE(TMemPoolTest);
  62. UNIT_TEST(TestMemPool)
  63. UNIT_TEST(TestAlign)
  64. UNIT_TEST(TestZeroArray)
  65. UNIT_TEST(TestLargeStartingAlign)
  66. UNIT_TEST(TestMoveAlloc)
  67. UNIT_TEST(TestRoundUpToNextPowerOfTwoOption)
  68. UNIT_TEST(TestMemoryPoolBookmark)
  69. UNIT_TEST_SUITE_END();
  70. private:
  71. inline void TestMemPool() {
  72. TCheckedAllocator alloc;
  73. {
  74. TMemoryPool pool(123, TMemoryPool::TExpGrow::Instance(), &alloc);
  75. for (size_t i = 0; i < 1000; ++i) {
  76. UNIT_ASSERT(pool.Allocate(i));
  77. }
  78. }
  79. alloc.CheckAtEnd();
  80. {
  81. TMemoryPool pool(150, TMemoryPool::TExpGrow::Instance(), &alloc);
  82. pool.Allocate(8);
  83. size_t memavail = pool.Available();
  84. size_t memwaste = pool.MemoryWaste();
  85. size_t memalloc = pool.MemoryAllocated();
  86. for (size_t i = 0; i < 1000; ++i) {
  87. void* m = pool.Allocate(i);
  88. UNIT_ASSERT(m);
  89. memset(m, 0, i);
  90. }
  91. UNIT_ASSERT_VALUES_EQUAL(pool.ClearReturnUsedChunkCount(true), 11);
  92. UNIT_ASSERT_VALUES_EQUAL(memalloc - 8, pool.MemoryAllocated());
  93. UNIT_ASSERT_VALUES_EQUAL(memwaste + 8, pool.MemoryWaste());
  94. UNIT_ASSERT_VALUES_EQUAL(memavail + 8, pool.Available());
  95. for (size_t i = 0; i < 1000; ++i) {
  96. void* m = pool.Allocate(i);
  97. UNIT_ASSERT(m);
  98. memset(m, 0, i);
  99. }
  100. UNIT_ASSERT_VALUES_EQUAL(pool.ClearReturnUsedChunkCount(false), 12);
  101. UNIT_ASSERT_VALUES_EQUAL(0, pool.MemoryAllocated());
  102. UNIT_ASSERT_VALUES_EQUAL(0, pool.MemoryWaste());
  103. UNIT_ASSERT_VALUES_EQUAL(0, pool.Available());
  104. }
  105. alloc.CheckAtEnd();
  106. struct TConstructorTest {
  107. int ConstructorType;
  108. TConstructorTest()
  109. : ConstructorType(1)
  110. {
  111. }
  112. TConstructorTest(int)
  113. : ConstructorType(2)
  114. {
  115. }
  116. TConstructorTest(const TString&, const TString&)
  117. : ConstructorType(3)
  118. {
  119. }
  120. TConstructorTest(TString&&, TString&&)
  121. : ConstructorType(4)
  122. {
  123. }
  124. };
  125. {
  126. TMemoryPool pool(123, TMemoryPool::TExpGrow::Instance(), &alloc);
  127. THolder<TConstructorTest, TDestructor> data1{pool.New<TConstructorTest>()};
  128. THolder<TConstructorTest, TDestructor> data2{pool.New<TConstructorTest>(42)};
  129. THolder<TConstructorTest, TDestructor> data3{pool.New<TConstructorTest>("hello", "world")};
  130. UNIT_ASSERT_VALUES_EQUAL(data1->ConstructorType, 1);
  131. UNIT_ASSERT_VALUES_EQUAL(data2->ConstructorType, 2);
  132. UNIT_ASSERT_VALUES_EQUAL(data3->ConstructorType, 4);
  133. }
  134. alloc.CheckAtEnd();
  135. }
  136. inline void TestAlign() {
  137. TMemoryPool pool(1);
  138. void* aligned16 = pool.Allocate(3, 16);
  139. void* aligned2 = pool.Allocate(3, 2);
  140. void* aligned128 = pool.Allocate(3, 128);
  141. void* aligned4 = pool.Allocate(3, 4);
  142. void* aligned256 = pool.Allocate(3, 256);
  143. void* aligned8 = pool.Allocate(3, 8);
  144. void* aligned1024 = pool.Allocate(3, 1024);
  145. UNIT_ASSERT_VALUES_UNEQUAL(aligned16, nullptr);
  146. UNIT_ASSERT_VALUES_UNEQUAL(aligned2, nullptr);
  147. UNIT_ASSERT_VALUES_UNEQUAL(aligned128, nullptr);
  148. UNIT_ASSERT_VALUES_UNEQUAL(aligned4, nullptr);
  149. UNIT_ASSERT_VALUES_UNEQUAL(aligned256, nullptr);
  150. UNIT_ASSERT_VALUES_UNEQUAL(aligned8, nullptr);
  151. UNIT_ASSERT_VALUES_UNEQUAL(aligned1024, nullptr);
  152. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned2) & 1, 0);
  153. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned4) & 3, 0);
  154. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned8) & 7, 0);
  155. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned16) & 15, 0);
  156. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned128) & 127, 0);
  157. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned256) & 255, 0);
  158. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned1024) & 1023, 0);
  159. }
  160. void TestZeroArray() {
  161. TMemoryPool pool(1);
  162. size_t size = 10;
  163. i32* intArray = pool.AllocateZeroArray<i32>(size);
  164. for (size_t i = 0; i < size; ++i) {
  165. UNIT_ASSERT(intArray[i] == 0);
  166. }
  167. size_t align = 256;
  168. ui8* byteArray = pool.AllocateZeroArray<ui8>(size, align);
  169. UNIT_ASSERT(size_t(byteArray) % align == 0);
  170. for (size_t i = 0; i < size; ++i) {
  171. UNIT_ASSERT(byteArray[i] == 0);
  172. }
  173. }
  174. void TestLargeStartingAlign() {
  175. TMemoryPool pool(1);
  176. void* aligned4k1 = pool.Allocate(1, 4096);
  177. void* aligned4k2 = pool.Allocate(1, 4096);
  178. UNIT_ASSERT_VALUES_UNEQUAL(aligned4k1, nullptr);
  179. UNIT_ASSERT_VALUES_UNEQUAL(aligned4k2, nullptr);
  180. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned4k1) & 4095, 0);
  181. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned4k2) & 4095, 0);
  182. }
  183. template <typename T>
  184. void CheckMoveAlloc() {
  185. TMemoryPool pool(10 * sizeof(T));
  186. TVector<T, TPoolAllocator> elems(&pool);
  187. elems.reserve(1);
  188. elems.emplace_back();
  189. elems.resize(100);
  190. }
  191. void TestMoveAlloc() {
  192. CheckMoveAlloc<TNoMove>();
  193. CheckMoveAlloc<TNoCopy>();
  194. CheckMoveAlloc<TErrorOnCopy>();
  195. }
  196. void TestRoundUpToNextPowerOfTwoOption() {
  197. const size_t MEMORY_POOL_BLOCK_SIZE = (1024 - 16) * 4096 - 16 - 16 - 32;
  198. class TFixedBlockSizeMemoryPoolPolicy final: public TMemoryPool::IGrowPolicy {
  199. public:
  200. size_t Next(size_t /*prev*/) const noexcept override {
  201. return MEMORY_POOL_BLOCK_SIZE;
  202. }
  203. };
  204. TFixedBlockSizeMemoryPoolPolicy allocationPolicy;
  205. class TTestAllocator final: public TDefaultAllocator {
  206. public:
  207. TBlock Allocate(size_t len) override {
  208. Size_ += len;
  209. return TDefaultAllocator::Allocate(len);
  210. }
  211. size_t GetSize() const {
  212. return Size_;
  213. }
  214. private:
  215. size_t Size_ = 0;
  216. };
  217. TTestAllocator allocator;
  218. TMemoryPool::TOptions options;
  219. options.RoundUpToNextPowerOfTwo = false;
  220. constexpr size_t EXPECTED_ALLOCATION_SIZE = MEMORY_POOL_BLOCK_SIZE + 32;
  221. TMemoryPool pool(MEMORY_POOL_BLOCK_SIZE, &allocationPolicy, &allocator, options);
  222. pool.Allocate(MEMORY_POOL_BLOCK_SIZE);
  223. UNIT_ASSERT_VALUES_EQUAL(EXPECTED_ALLOCATION_SIZE, allocator.GetSize());
  224. pool.Allocate(1);
  225. UNIT_ASSERT_VALUES_EQUAL(2 * EXPECTED_ALLOCATION_SIZE, allocator.GetSize());
  226. }
  227. void TestMemoryPoolBookmark() {
  228. TCheckedAllocator alloc;
  229. {
  230. TMemoryPool pool(200U, TMemoryPool::TExpGrow::Instance(), &alloc);
  231. ui64* someData = pool.Allocate<ui64>();
  232. static const ui64 TESTING{0x123456789ABCDEF};
  233. *someData = TESTING;
  234. const auto ma = pool.MemoryAllocated();
  235. const auto chunkOverhead = ma - sizeof(ui64);
  236. const auto firstChunkTotal = pool.MemoryWaste() + ma;
  237. // Allocate some memory in pool but not enough to need new chunks:
  238. {
  239. TMemoryPool::TBookmark bookmarkA(pool);
  240. for (size_t i = 0U; i != 10; ++i) {
  241. UNIT_ASSERT(pool.Allocate<ui64>());
  242. }
  243. }
  244. UNIT_ASSERT_VALUES_EQUAL(pool.MemoryAllocated(), ma);
  245. UNIT_ASSERT_VALUES_EQUAL(*someData, TESTING);
  246. // Allocate some memory in pool enough to need a new single chunk:
  247. {
  248. TMemoryPool::TBookmark bookmarkB(pool);
  249. for (size_t i = 0U; i != 50; ++i) {
  250. UNIT_ASSERT(pool.Allocate<ui64>());
  251. }
  252. }
  253. UNIT_ASSERT_VALUES_EQUAL(pool.MemoryAllocated(), firstChunkTotal + chunkOverhead); // The last (second) chunk is completely free
  254. UNIT_ASSERT_VALUES_EQUAL(*someData, TESTING);
  255. }
  256. alloc.CheckAtEnd();
  257. }
  258. };
  259. UNIT_TEST_SUITE_REGISTRATION(TMemPoolTest);