pool.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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. inline TMemoryPool(size_t initial, IGrowPolicy* grow = TExpGrow::Instance(), IAllocator* alloc = TDefaultAllocator::Instance(), const TOptions& options = TOptions())
  106. : Current_(&Empty_)
  107. , BlockSize_(initial)
  108. , GrowPolicy_(grow)
  109. , Alloc_(alloc)
  110. , Options_(options)
  111. , Origin_(initial)
  112. , MemoryAllocatedBeforeCurrent_(0)
  113. , MemoryWasteBeforeCurrent_(0)
  114. {
  115. }
  116. inline ~TMemoryPool() {
  117. Clear();
  118. }
  119. inline void* Allocate(size_t len) {
  120. return RawAllocate(AlignUp<size_t>(len, PLATFORM_DATA_ALIGN));
  121. }
  122. inline void* Allocate(size_t len, size_t align) {
  123. return RawAllocate(AlignUp<size_t>(len, PLATFORM_DATA_ALIGN), align);
  124. }
  125. template <typename T>
  126. inline T* Allocate() {
  127. return (T*)this->Allocate(sizeof(T), alignof(T));
  128. }
  129. template <typename T>
  130. inline T* Allocate(size_t align) {
  131. return (T*)this->Allocate(sizeof(T), Max(align, alignof(T)));
  132. }
  133. template <typename T>
  134. inline T* AllocateArray(size_t count) {
  135. return (T*)this->Allocate(sizeof(T) * count, alignof(T));
  136. }
  137. template <typename T>
  138. inline T* AllocateArray(size_t count, size_t align) {
  139. return (T*)this->Allocate(sizeof(T) * count, Max(align, alignof(T)));
  140. }
  141. template <typename T>
  142. inline T* AllocateZeroArray(size_t count) {
  143. T* ptr = AllocateArray<T>(count);
  144. memset(ptr, 0, count * sizeof(T));
  145. return ptr;
  146. }
  147. template <typename T>
  148. inline T* AllocateZeroArray(size_t count, size_t align) {
  149. T* ptr = AllocateArray<T>(count, align);
  150. memset(ptr, 0, count * sizeof(T));
  151. return ptr;
  152. }
  153. template <typename T, typename... Args>
  154. inline T* New(Args&&... args) {
  155. return new (Allocate<T>()) T(std::forward<Args>(args)...);
  156. }
  157. template <typename T>
  158. inline T* NewArray(size_t count) {
  159. T* arr = (T*)AllocateArray<T>(count);
  160. for (T *ptr = arr, *end = arr + count; ptr != end; ++ptr) {
  161. new (ptr) T;
  162. }
  163. return arr;
  164. }
  165. template <typename TChar>
  166. inline TChar* Append(const TChar* str) {
  167. return Append(str, std::char_traits<TChar>::length(str) + 1); // include terminating zero byte
  168. }
  169. template <typename TChar>
  170. inline TChar* Append(const TChar* str, size_t len) {
  171. TChar* ret = AllocateArray<TChar>(len);
  172. std::char_traits<TChar>::copy(ret, str, len);
  173. return ret;
  174. }
  175. template <typename TChar>
  176. inline TBasicStringBuf<TChar> AppendString(const TBasicStringBuf<TChar>& buf) {
  177. return TBasicStringBuf<TChar>(Append(buf.data(), buf.size()), buf.size());
  178. }
  179. template <typename TChar>
  180. inline TBasicStringBuf<TChar> AppendCString(const TBasicStringBuf<TChar>& buf) {
  181. TChar* ret = static_cast<TChar*>(Allocate((buf.size() + 1) * sizeof(TChar)));
  182. std::char_traits<TChar>::copy(ret, buf.data(), buf.size());
  183. *(ret + buf.size()) = 0;
  184. return TBasicStringBuf<TChar>(ret, buf.size());
  185. }
  186. inline size_t Available() const noexcept {
  187. return Current_->Left();
  188. }
  189. inline void Clear() noexcept {
  190. DoClear(false);
  191. }
  192. inline void ClearKeepFirstChunk() noexcept {
  193. DoClear(true);
  194. }
  195. inline size_t ClearReturnUsedChunkCount(bool keepFirstChunk) noexcept {
  196. return DoClear(keepFirstChunk);
  197. }
  198. inline size_t MemoryAllocated() const noexcept {
  199. return MemoryAllocatedBeforeCurrent_ + (Current_ != &Empty_ ? Current_->Used() : 0);
  200. }
  201. inline size_t MemoryWaste() const noexcept {
  202. return MemoryWasteBeforeCurrent_ + (Current_ != &Empty_ ? Current_->Left() : 0);
  203. }
  204. template <class TOp>
  205. inline void Traverse(TOp& op) const noexcept {
  206. for (TChunkList::TConstIterator i = Chunks_.Begin(); i != Chunks_.End(); ++i) {
  207. op(i->Data(), i->DataSize());
  208. }
  209. }
  210. inline IAllocator* RealAllocator() const noexcept {
  211. return Alloc_;
  212. }
  213. protected:
  214. inline void* RawAllocate(size_t len) {
  215. void* ret = Current_->Allocate(len);
  216. if (ret) {
  217. return ret;
  218. }
  219. AddChunk(len);
  220. return Current_->Allocate(len);
  221. }
  222. inline void* RawAllocate(size_t len, size_t align) {
  223. Y_ASSERT(align > 0);
  224. void* ret = Current_->Allocate(len, align);
  225. if (ret) {
  226. return ret;
  227. }
  228. AddChunk(len + align - 1);
  229. return Current_->Allocate(len, align);
  230. }
  231. private:
  232. void AddChunk(size_t hint);
  233. size_t DoClear(bool keepfirst) noexcept;
  234. private:
  235. TChunk Empty_;
  236. TChunk* Current_;
  237. size_t BlockSize_;
  238. IGrowPolicy* GrowPolicy_;
  239. IAllocator* Alloc_;
  240. TOptions Options_;
  241. TChunkList Chunks_;
  242. const size_t Origin_;
  243. size_t MemoryAllocatedBeforeCurrent_;
  244. size_t MemoryWasteBeforeCurrent_;
  245. };
  246. template <typename TPool>
  247. struct TPoolableBase {
  248. inline void* operator new(size_t bytes, TPool& pool) {
  249. return pool.Allocate(bytes);
  250. }
  251. inline void* operator new(size_t bytes, std::align_val_t align, TPool& pool) {
  252. return pool.Allocate(bytes, (size_t)align);
  253. }
  254. inline void operator delete(void*, size_t) noexcept {
  255. }
  256. inline void operator delete(void*, TPool&) noexcept {
  257. }
  258. private:
  259. inline void* operator new(size_t); // disallow default allocation
  260. };
  261. struct TPoolable: public TPoolableBase<TMemoryPool> {
  262. };
  263. class TMemoryPoolAllocator: public IAllocator {
  264. public:
  265. inline TMemoryPoolAllocator(TMemoryPool* pool)
  266. : Pool_(pool)
  267. {
  268. }
  269. TBlock Allocate(size_t len) override {
  270. TBlock ret = {Pool_->Allocate(len), len};
  271. return ret;
  272. }
  273. void Release(const TBlock& block) override {
  274. (void)block;
  275. }
  276. private:
  277. TMemoryPool* Pool_;
  278. };
  279. template <class T, class TPool>
  280. class TPoolAllocBase {
  281. public:
  282. using pointer = T*;
  283. using const_pointer = const T*;
  284. using reference = T&;
  285. using const_reference = const T&;
  286. using size_type = size_t;
  287. using difference_type = ptrdiff_t;
  288. using value_type = T;
  289. inline TPoolAllocBase(TPool* pool)
  290. : Pool_(pool)
  291. {
  292. }
  293. template <typename TOther>
  294. inline TPoolAllocBase(const TPoolAllocBase<TOther, TPool>& o)
  295. : Pool_(o.GetPool())
  296. {
  297. }
  298. inline T* allocate(size_t n) {
  299. return (T*)Pool_->Allocate(n * sizeof(T), alignof(T));
  300. }
  301. inline void deallocate(pointer /*p*/, size_t /*n*/) {
  302. }
  303. template <class T1>
  304. struct rebind {
  305. using other = TPoolAllocBase<T1, TPool>;
  306. };
  307. inline size_type max_size() const noexcept {
  308. return size_type(-1) / sizeof(T);
  309. }
  310. template <typename... Args>
  311. inline void construct(pointer p, Args&&... args) {
  312. new (p) T(std::forward<Args>(args)...);
  313. }
  314. inline void destroy(pointer p) noexcept {
  315. (void)p; /* Make MSVC happy. */
  316. p->~T();
  317. }
  318. inline TPool* GetPool() const {
  319. return Pool_;
  320. }
  321. inline friend bool operator==(const TPoolAllocBase& l, const TPoolAllocBase& r) {
  322. return l.Pool_ == r.Pool_;
  323. }
  324. inline friend bool operator!=(const TPoolAllocBase& l, const TPoolAllocBase& r) {
  325. return !(l == r);
  326. }
  327. private:
  328. TPool* Pool_;
  329. };
  330. template <class T>
  331. using TPoolAlloc = TPoolAllocBase<T, TMemoryPool>;
  332. // Any type since it is supposed to be rebound anyway.
  333. using TPoolAllocator = TPoolAlloc<int>;
  334. template <class T>
  335. inline bool operator==(const TPoolAlloc<T>&, const TPoolAlloc<T>&) noexcept {
  336. return true;
  337. }
  338. template <class T>
  339. inline bool operator!=(const TPoolAlloc<T>&, const TPoolAlloc<T>&) noexcept {
  340. return false;
  341. }