pool_ut.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  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_SUITE_END();
  69. private:
  70. inline void TestMemPool() {
  71. TCheckedAllocator alloc;
  72. {
  73. TMemoryPool pool(123, TMemoryPool::TExpGrow::Instance(), &alloc);
  74. for (size_t i = 0; i < 1000; ++i) {
  75. UNIT_ASSERT(pool.Allocate(i));
  76. }
  77. }
  78. alloc.CheckAtEnd();
  79. {
  80. TMemoryPool pool(150, TMemoryPool::TExpGrow::Instance(), &alloc);
  81. pool.Allocate(8);
  82. size_t memavail = pool.Available();
  83. size_t memwaste = pool.MemoryWaste();
  84. size_t memalloc = pool.MemoryAllocated();
  85. for (size_t i = 0; i < 1000; ++i) {
  86. void* m = pool.Allocate(i);
  87. UNIT_ASSERT(m);
  88. memset(m, 0, i);
  89. }
  90. pool.ClearKeepFirstChunk();
  91. UNIT_ASSERT_VALUES_EQUAL(memalloc - 8, pool.MemoryAllocated());
  92. UNIT_ASSERT_VALUES_EQUAL(memwaste + 8, pool.MemoryWaste());
  93. UNIT_ASSERT_VALUES_EQUAL(memavail + 8, pool.Available());
  94. for (size_t i = 0; i < 1000; ++i) {
  95. void* m = pool.Allocate(i);
  96. UNIT_ASSERT(m);
  97. memset(m, 0, i);
  98. }
  99. pool.Clear();
  100. UNIT_ASSERT_VALUES_EQUAL(0, pool.MemoryAllocated());
  101. UNIT_ASSERT_VALUES_EQUAL(0, pool.MemoryWaste());
  102. UNIT_ASSERT_VALUES_EQUAL(0, pool.Available());
  103. }
  104. alloc.CheckAtEnd();
  105. struct TConstructorTest {
  106. int ConstructorType;
  107. TConstructorTest()
  108. : ConstructorType(1)
  109. {
  110. }
  111. TConstructorTest(int)
  112. : ConstructorType(2)
  113. {
  114. }
  115. TConstructorTest(const TString&, const TString&)
  116. : ConstructorType(3)
  117. {
  118. }
  119. TConstructorTest(TString&&, TString&&)
  120. : ConstructorType(4)
  121. {
  122. }
  123. };
  124. {
  125. TMemoryPool pool(123, TMemoryPool::TExpGrow::Instance(), &alloc);
  126. THolder<TConstructorTest, TDestructor> data1{pool.New<TConstructorTest>()};
  127. THolder<TConstructorTest, TDestructor> data2{pool.New<TConstructorTest>(42)};
  128. THolder<TConstructorTest, TDestructor> data3{pool.New<TConstructorTest>("hello", "world")};
  129. UNIT_ASSERT_VALUES_EQUAL(data1->ConstructorType, 1);
  130. UNIT_ASSERT_VALUES_EQUAL(data2->ConstructorType, 2);
  131. UNIT_ASSERT_VALUES_EQUAL(data3->ConstructorType, 4);
  132. }
  133. alloc.CheckAtEnd();
  134. }
  135. inline void TestAlign() {
  136. TMemoryPool pool(1);
  137. void* aligned16 = pool.Allocate(3, 16);
  138. void* aligned2 = pool.Allocate(3, 2);
  139. void* aligned128 = pool.Allocate(3, 128);
  140. void* aligned4 = pool.Allocate(3, 4);
  141. void* aligned256 = pool.Allocate(3, 256);
  142. void* aligned8 = pool.Allocate(3, 8);
  143. void* aligned1024 = pool.Allocate(3, 1024);
  144. UNIT_ASSERT_VALUES_UNEQUAL(aligned16, nullptr);
  145. UNIT_ASSERT_VALUES_UNEQUAL(aligned2, nullptr);
  146. UNIT_ASSERT_VALUES_UNEQUAL(aligned128, nullptr);
  147. UNIT_ASSERT_VALUES_UNEQUAL(aligned4, nullptr);
  148. UNIT_ASSERT_VALUES_UNEQUAL(aligned256, nullptr);
  149. UNIT_ASSERT_VALUES_UNEQUAL(aligned8, nullptr);
  150. UNIT_ASSERT_VALUES_UNEQUAL(aligned1024, nullptr);
  151. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned2) & 1, 0);
  152. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned4) & 3, 0);
  153. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned8) & 7, 0);
  154. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned16) & 15, 0);
  155. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned128) & 127, 0);
  156. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned256) & 255, 0);
  157. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned1024) & 1023, 0);
  158. }
  159. void TestZeroArray() {
  160. TMemoryPool pool(1);
  161. size_t size = 10;
  162. i32* intArray = pool.AllocateZeroArray<i32>(size);
  163. for (size_t i = 0; i < size; ++i) {
  164. UNIT_ASSERT(intArray[i] == 0);
  165. }
  166. size_t align = 256;
  167. ui8* byteArray = pool.AllocateZeroArray<ui8>(size, align);
  168. UNIT_ASSERT(size_t(byteArray) % align == 0);
  169. for (size_t i = 0; i < size; ++i) {
  170. UNIT_ASSERT(byteArray[i] == 0);
  171. }
  172. }
  173. void TestLargeStartingAlign() {
  174. TMemoryPool pool(1);
  175. void* aligned4k1 = pool.Allocate(1, 4096);
  176. void* aligned4k2 = pool.Allocate(1, 4096);
  177. UNIT_ASSERT_VALUES_UNEQUAL(aligned4k1, nullptr);
  178. UNIT_ASSERT_VALUES_UNEQUAL(aligned4k2, nullptr);
  179. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned4k1) & 4095, 0);
  180. UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast<uintptr_t>(aligned4k2) & 4095, 0);
  181. }
  182. template <typename T>
  183. void CheckMoveAlloc() {
  184. TMemoryPool pool(10 * sizeof(T));
  185. TVector<T, TPoolAllocator> elems(&pool);
  186. elems.reserve(1);
  187. elems.emplace_back();
  188. elems.resize(100);
  189. }
  190. void TestMoveAlloc() {
  191. CheckMoveAlloc<TNoMove>();
  192. CheckMoveAlloc<TNoCopy>();
  193. CheckMoveAlloc<TErrorOnCopy>();
  194. }
  195. void TestRoundUpToNextPowerOfTwoOption() {
  196. const size_t MEMORY_POOL_BLOCK_SIZE = (1024 - 16) * 4096 - 16 - 16 - 32;
  197. class TFixedBlockSizeMemoryPoolPolicy final: public TMemoryPool::IGrowPolicy {
  198. public:
  199. size_t Next(size_t /*prev*/) const noexcept override {
  200. return MEMORY_POOL_BLOCK_SIZE;
  201. }
  202. };
  203. TFixedBlockSizeMemoryPoolPolicy allocationPolicy;
  204. class TTestAllocator final: public TDefaultAllocator {
  205. public:
  206. TBlock Allocate(size_t len) override {
  207. Size_ += len;
  208. return TDefaultAllocator::Allocate(len);
  209. }
  210. size_t GetSize() const {
  211. return Size_;
  212. }
  213. private:
  214. size_t Size_ = 0;
  215. };
  216. TTestAllocator allocator;
  217. TMemoryPool::TOptions options;
  218. options.RoundUpToNextPowerOfTwo = false;
  219. constexpr size_t EXPECTED_ALLOCATION_SIZE = MEMORY_POOL_BLOCK_SIZE + 32;
  220. TMemoryPool pool(MEMORY_POOL_BLOCK_SIZE, &allocationPolicy, &allocator, options);
  221. pool.Allocate(MEMORY_POOL_BLOCK_SIZE);
  222. UNIT_ASSERT_VALUES_EQUAL(EXPECTED_ALLOCATION_SIZE, allocator.GetSize());
  223. pool.Allocate(1);
  224. UNIT_ASSERT_VALUES_EQUAL(2 * EXPECTED_ALLOCATION_SIZE, allocator.GetSize());
  225. }
  226. };
  227. UNIT_TEST_SUITE_REGISTRATION(TMemPoolTest);