pool.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. #pragma once
  2. #include "alloc.h"
  3. #include <util/system/align.h>
  4. #include <util/system/yassert.h>
  5. #include <util/generic/bitops.h>
  6. #include <util/generic/utility.h>
  7. #include <util/generic/intrlist.h>
  8. #include <util/generic/strbuf.h>
  9. #include <util/generic/singleton.h>
  10. #include <new>
  11. #include <string>
  12. #include <utility>
  13. /**
  14. * Memory pool implements a memory allocation scheme that is very fast, but
  15. * limited in its usage.
  16. *
  17. * A common use case is when you want to allocate a bunch of small objects, and
  18. * then release them all at some point of your program. Using memory pool, you
  19. * can just drop them off into oblivion without calling any destructors,
  20. * provided that all associated memory was allocated on the pool.
  21. */
  22. class TMemoryPool {
  23. private:
  24. using TBlock = IAllocator::TBlock;
  25. class TChunk: public TIntrusiveListItem<TChunk> {
  26. public:
  27. inline TChunk(size_t len = 0) noexcept
  28. : Cur_((char*)(this + 1))
  29. , Left_(len)
  30. {
  31. Y_ASSERT((((size_t)Cur_) % PLATFORM_DATA_ALIGN) == 0);
  32. }
  33. inline void* Allocate(size_t len) noexcept {
  34. if (Left_ >= len) {
  35. char* ret = Cur_;
  36. Cur_ += len;
  37. Left_ -= len;
  38. return ret;
  39. }
  40. return nullptr;
  41. }
  42. inline void* Allocate(size_t len, size_t align) noexcept {
  43. size_t pad = AlignUp(Cur_, align) - Cur_;
  44. void* ret = Allocate(pad + len);
  45. if (ret) {
  46. return static_cast<char*>(ret) + pad;
  47. }
  48. return nullptr;
  49. }
  50. inline size_t BlockLength() const noexcept {
  51. return (Cur_ + Left_) - (char*)this;
  52. }
  53. inline size_t Used() const noexcept {
  54. return Cur_ - (const char*)this;
  55. }
  56. inline size_t Left() const noexcept {
  57. return Left_;
  58. }
  59. inline const char* Data() const noexcept {
  60. return (const char*)(this + 1);
  61. }
  62. inline char* Data() noexcept {
  63. return (char*)(this + 1);
  64. }
  65. inline size_t DataSize() const noexcept {
  66. return Cur_ - Data();
  67. }
  68. inline void ResetChunk() noexcept {
  69. size_t total = DataSize() + Left();
  70. Cur_ = Data();
  71. Left_ = total;
  72. }
  73. private:
  74. char* Cur_;
  75. size_t Left_;
  76. };
  77. using TChunkList = TIntrusiveList<TChunk>;
  78. public:
  79. class IGrowPolicy {
  80. public:
  81. virtual ~IGrowPolicy() = default;
  82. virtual size_t Next(size_t prev) const noexcept = 0;
  83. };
  84. class TLinearGrow: public IGrowPolicy {
  85. public:
  86. size_t Next(size_t prev) const noexcept override {
  87. return prev;
  88. }
  89. static IGrowPolicy* Instance() noexcept;
  90. };
  91. class TExpGrow: public IGrowPolicy {
  92. public:
  93. size_t Next(size_t prev) const noexcept override {
  94. return prev * 2;
  95. }
  96. static IGrowPolicy* Instance() noexcept;
  97. };
  98. struct TOptions {
  99. bool RoundUpToNextPowerOfTwo;
  100. TOptions()
  101. : RoundUpToNextPowerOfTwo(true)
  102. {
  103. }
  104. };
  105. // When a bookmark is destroyed, the memory pool returns to the state when the bookmark was created.
  106. class TBookmark {
  107. public:
  108. inline TBookmark(TMemoryPool& memoryPool)
  109. : OwnerPoolRef_(memoryPool)
  110. , BookmarkChunk_(memoryPool.Current_)
  111. , BookmarkChunkDataSize_(memoryPool.Current_->DataSize())
  112. {
  113. }
  114. inline ~TBookmark() {
  115. OwnerPoolRef_.Current_->ResetChunk();
  116. if (OwnerPoolRef_.Current_ == BookmarkChunk_) {
  117. Y_UNUSED(BookmarkChunk_->Allocate(BookmarkChunkDataSize_));
  118. }
  119. }
  120. private:
  121. TMemoryPool& OwnerPoolRef_;
  122. TMemoryPool::TChunk* BookmarkChunk_;
  123. size_t BookmarkChunkDataSize_;
  124. };
  125. inline TMemoryPool(size_t initial, IGrowPolicy* grow = TExpGrow::Instance(), IAllocator* alloc = TDefaultAllocator::Instance(), const TOptions& options = TOptions())
  126. : Current_(&Empty_)
  127. , BlockSize_(initial)
  128. , GrowPolicy_(grow)
  129. , Alloc_(alloc)
  130. , Options_(options)
  131. , Origin_(initial)
  132. , MemoryAllocatedBeforeCurrent_(0)
  133. , MemoryWasteBeforeCurrent_(0)
  134. {
  135. }
  136. inline ~TMemoryPool() {
  137. Clear();
  138. }
  139. inline void* Allocate(size_t len) {
  140. return RawAllocate(AlignUp<size_t>(len, PLATFORM_DATA_ALIGN));
  141. }
  142. inline void* Allocate(size_t len, size_t align) {
  143. return RawAllocate(AlignUp<size_t>(len, PLATFORM_DATA_ALIGN), align);
  144. }
  145. template <typename T>
  146. inline T* Allocate() {
  147. return (T*)this->Allocate(sizeof(T), alignof(T));
  148. }
  149. template <typename T>
  150. inline T* Allocate(size_t align) {
  151. return (T*)this->Allocate(sizeof(T), Max(align, alignof(T)));
  152. }
  153. template <typename T>
  154. inline T* AllocateArray(size_t count) {
  155. return (T*)this->Allocate(sizeof(T) * count, alignof(T));
  156. }
  157. template <typename T>
  158. inline T* AllocateArray(size_t count, size_t align) {
  159. return (T*)this->Allocate(sizeof(T) * count, Max(align, alignof(T)));
  160. }
  161. template <typename T>
  162. inline T* AllocateZeroArray(size_t count) {
  163. T* ptr = AllocateArray<T>(count);
  164. memset(ptr, 0, count * sizeof(T));
  165. return ptr;
  166. }
  167. template <typename T>
  168. inline T* AllocateZeroArray(size_t count, size_t align) {
  169. T* ptr = AllocateArray<T>(count, align);
  170. memset(ptr, 0, count * sizeof(T));
  171. return ptr;
  172. }
  173. template <typename T, typename... Args>
  174. inline T* New(Args&&... args) {
  175. return new (Allocate<T>()) T(std::forward<Args>(args)...);
  176. }
  177. template <typename T>
  178. inline T* NewArray(size_t count) {
  179. T* arr = (T*)AllocateArray<T>(count);
  180. for (T *ptr = arr, *end = arr + count; ptr != end; ++ptr) {
  181. new (ptr) T;
  182. }
  183. return arr;
  184. }
  185. template <typename TChar>
  186. inline TChar* Append(const TChar* str) {
  187. return Append(str, std::char_traits<TChar>::length(str) + 1); // include terminating zero byte
  188. }
  189. template <typename TChar>
  190. inline TChar* Append(const TChar* str, size_t len) {
  191. TChar* ret = AllocateArray<TChar>(len);
  192. std::char_traits<TChar>::copy(ret, str, len);
  193. return ret;
  194. }
  195. template <typename TChar>
  196. inline TBasicStringBuf<TChar> AppendString(const TBasicStringBuf<TChar>& buf) {
  197. return TBasicStringBuf<TChar>(Append(buf.data(), buf.size()), buf.size());
  198. }
  199. template <typename TChar>
  200. inline TBasicStringBuf<TChar> AppendCString(const TBasicStringBuf<TChar>& buf) {
  201. TChar* ret = static_cast<TChar*>(Allocate((buf.size() + 1) * sizeof(TChar)));
  202. std::char_traits<TChar>::copy(ret, buf.data(), buf.size());
  203. *(ret + buf.size()) = 0;
  204. return TBasicStringBuf<TChar>(ret, buf.size());
  205. }
  206. inline size_t Available() const noexcept {
  207. return Current_->Left();
  208. }
  209. inline void Clear() noexcept {
  210. DoClear(false);
  211. }
  212. inline void ClearKeepFirstChunk() noexcept {
  213. DoClear(true);
  214. }
  215. inline size_t ClearReturnUsedChunkCount(bool keepFirstChunk) noexcept {
  216. return DoClear(keepFirstChunk);
  217. }
  218. inline size_t MemoryAllocated() const noexcept {
  219. return MemoryAllocatedBeforeCurrent_ + (Current_ != &Empty_ ? Current_->Used() : 0);
  220. }
  221. inline size_t MemoryWaste() const noexcept {
  222. return MemoryWasteBeforeCurrent_ + (Current_ != &Empty_ ? Current_->Left() : 0);
  223. }
  224. template <class TOp>
  225. inline void Traverse(TOp& op) const noexcept {
  226. for (TChunkList::TConstIterator i = Chunks_.Begin(); i != Chunks_.End(); ++i) {
  227. op(i->Data(), i->DataSize());
  228. }
  229. }
  230. inline IAllocator* RealAllocator() const noexcept {
  231. return Alloc_;
  232. }
  233. protected:
  234. inline void* RawAllocate(size_t len) {
  235. void* ret = Current_->Allocate(len);
  236. if (ret) {
  237. return ret;
  238. }
  239. AddChunk(len);
  240. return Current_->Allocate(len);
  241. }
  242. inline void* RawAllocate(size_t len, size_t align) {
  243. Y_ASSERT(align > 0);
  244. void* ret = Current_->Allocate(len, align);
  245. if (ret) {
  246. return ret;
  247. }
  248. AddChunk(len + align - 1);
  249. return Current_->Allocate(len, align);
  250. }
  251. private:
  252. void AddChunk(size_t hint);
  253. size_t DoClear(bool keepfirst) noexcept;
  254. private:
  255. TChunk Empty_;
  256. TChunk* Current_;
  257. size_t BlockSize_;
  258. IGrowPolicy* GrowPolicy_;
  259. IAllocator* Alloc_;
  260. TOptions Options_;
  261. TChunkList Chunks_;
  262. const size_t Origin_;
  263. size_t MemoryAllocatedBeforeCurrent_;
  264. size_t MemoryWasteBeforeCurrent_;
  265. };
  266. template <typename TPool>
  267. struct TPoolableBase {
  268. inline void* operator new(size_t bytes, TPool& pool) {
  269. return pool.Allocate(bytes);
  270. }
  271. inline void* operator new(size_t bytes, std::align_val_t align, TPool& pool) {
  272. return pool.Allocate(bytes, (size_t)align);
  273. }
  274. inline void operator delete(void*, size_t) noexcept {
  275. }
  276. inline void operator delete(void*, TPool&) noexcept {
  277. }
  278. private:
  279. inline void* operator new(size_t); // disallow default allocation
  280. };
  281. struct TPoolable: public TPoolableBase<TMemoryPool> {
  282. };
  283. class TMemoryPoolAllocator: public IAllocator {
  284. public:
  285. inline TMemoryPoolAllocator(TMemoryPool* pool)
  286. : Pool_(pool)
  287. {
  288. }
  289. TBlock Allocate(size_t len) override {
  290. TBlock ret = {Pool_->Allocate(len), len};
  291. return ret;
  292. }
  293. void Release(const TBlock& block) override {
  294. (void)block;
  295. }
  296. private:
  297. TMemoryPool* Pool_;
  298. };
  299. template <class T, class TPool>
  300. class TPoolAllocBase {
  301. public:
  302. using pointer = T*;
  303. using const_pointer = const T*;
  304. using reference = T&;
  305. using const_reference = const T&;
  306. using size_type = size_t;
  307. using difference_type = ptrdiff_t;
  308. using value_type = T;
  309. inline TPoolAllocBase(TPool* pool)
  310. : Pool_(pool)
  311. {
  312. }
  313. template <typename TOther>
  314. inline TPoolAllocBase(const TPoolAllocBase<TOther, TPool>& o)
  315. : Pool_(o.GetPool())
  316. {
  317. }
  318. inline T* allocate(size_t n) {
  319. return (T*)Pool_->Allocate(n * sizeof(T), alignof(T));
  320. }
  321. inline void deallocate(pointer /*p*/, size_t /*n*/) {
  322. }
  323. template <class T1>
  324. struct rebind {
  325. using other = TPoolAllocBase<T1, TPool>;
  326. };
  327. inline size_type max_size() const noexcept {
  328. return size_type(-1) / sizeof(T);
  329. }
  330. template <typename... Args>
  331. inline void construct(pointer p, Args&&... args) {
  332. new (p) T(std::forward<Args>(args)...);
  333. }
  334. inline void destroy(pointer p) noexcept {
  335. (void)p; /* Make MSVC happy. */
  336. p->~T();
  337. }
  338. inline TPool* GetPool() const {
  339. return Pool_;
  340. }
  341. inline friend bool operator==(const TPoolAllocBase& l, const TPoolAllocBase& r) {
  342. return l.Pool_ == r.Pool_;
  343. }
  344. inline friend bool operator!=(const TPoolAllocBase& l, const TPoolAllocBase& r) {
  345. return !(l == r);
  346. }
  347. private:
  348. TPool* Pool_;
  349. };
  350. template <class T>
  351. using TPoolAlloc = TPoolAllocBase<T, TMemoryPool>;
  352. // Any type since it is supposed to be rebound anyway.
  353. using TPoolAllocator = TPoolAlloc<int>;
  354. template <class T>
  355. inline bool operator==(const TPoolAlloc<T>&, const TPoolAlloc<T>&) noexcept {
  356. return true;
  357. }
  358. template <class T>
  359. inline bool operator!=(const TPoolAlloc<T>&, const TPoolAlloc<T>&) noexcept {
  360. return false;
  361. }