aligned_page_pool.h 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #pragma once
  2. #include <util/generic/yexception.h>
  3. #include <util/stream/output.h>
  4. #include <util/string/builder.h>
  5. #include <util/system/yassert.h>
  6. #include <util/system/defaults.h>
  7. #include <library/cpp/monlib/dynamic_counters/counters.h>
  8. #include <type_traits>
  9. #include <stack>
  10. #include <vector>
  11. #include <unordered_set>
  12. #include <unordered_map>
  13. namespace NKikimr {
  14. struct TAlignedPagePoolCounters {
  15. explicit TAlignedPagePoolCounters(::NMonitoring::TDynamicCounterPtr countersRoot = nullptr, const TString& name = TString());
  16. ::NMonitoring::TDynamicCounters::TCounterPtr TotalBytesAllocatedCntr;
  17. ::NMonitoring::TDynamicCounters::TCounterPtr AllocationsCntr;
  18. ::NMonitoring::TDynamicCounters::TCounterPtr PoolsCntr;
  19. ::NMonitoring::TDynamicCounters::TCounterPtr LostPagesBytesFreeCntr;
  20. void Swap(TAlignedPagePoolCounters& other) {
  21. DoSwap(TotalBytesAllocatedCntr, other.TotalBytesAllocatedCntr);
  22. DoSwap(AllocationsCntr, other.AllocationsCntr);
  23. DoSwap(PoolsCntr, other.PoolsCntr);
  24. DoSwap(LostPagesBytesFreeCntr, other.LostPagesBytesFreeCntr);
  25. }
  26. };
  27. // NOTE: We intentionally avoid inheritance from std::exception here to make it harder
  28. // to catch this exception in UDFs code, so we can handle it in the host.
  29. class TMemoryLimitExceededException {
  30. public:
  31. virtual ~TMemoryLimitExceededException() = default;
  32. };
  33. class TSystemMmap {
  34. public:
  35. static void* Mmap(size_t size);
  36. static int Munmap(void* addr, size_t size);
  37. };
  38. class TFakeAlignedMmap {
  39. public:
  40. static std::function<void(size_t size)> OnMmap;
  41. static std::function<void(void* addr, size_t size)> OnMunmap;
  42. static void* Mmap(size_t size);
  43. static int Munmap(void* addr, size_t size);
  44. };
  45. class TFakeUnalignedMmap {
  46. public:
  47. static std::function<void(size_t size)> OnMmap;
  48. static std::function<void(void* addr, size_t size)> OnMunmap;
  49. static void* Mmap(size_t size);
  50. static int Munmap(void* addr, size_t size);
  51. };
  52. template<typename TMmap = TSystemMmap>
  53. class TAlignedPagePoolImpl {
  54. public:
  55. static constexpr ui64 POOL_PAGE_SIZE = 1ULL << 16; // 64k
  56. static constexpr ui64 PAGE_ADDR_MASK = ~(POOL_PAGE_SIZE - 1);
  57. static constexpr ui64 ALLOC_AHEAD_PAGES = 31;
  58. explicit TAlignedPagePoolImpl(const TSourceLocation& location,
  59. const TAlignedPagePoolCounters& counters = TAlignedPagePoolCounters())
  60. : Counters(counters)
  61. , DebugInfo(location)
  62. {
  63. if (Counters.PoolsCntr) {
  64. ++(*Counters.PoolsCntr);
  65. }
  66. }
  67. TAlignedPagePoolImpl(const TAlignedPagePoolImpl&) = delete;
  68. TAlignedPagePoolImpl(TAlignedPagePoolImpl&& other) = delete;
  69. TAlignedPagePoolImpl& operator = (const TAlignedPagePoolImpl&) = delete;
  70. TAlignedPagePoolImpl& operator = (TAlignedPagePoolImpl&& other) = delete;
  71. ~TAlignedPagePoolImpl();
  72. inline size_t GetAllocated() const noexcept {
  73. return TotalAllocated;
  74. }
  75. inline size_t GetUsed() const noexcept {
  76. return TotalAllocated - GetFreePageCount() * POOL_PAGE_SIZE;
  77. }
  78. inline size_t GetFreePageCount() const noexcept {
  79. return FreePages.size();
  80. }
  81. static inline const void* GetPageStart(const void* addr) noexcept {
  82. return reinterpret_cast<const void*>(reinterpret_cast<uintptr_t>(addr) & PAGE_ADDR_MASK);
  83. }
  84. static inline void* GetPageStart(void* addr) noexcept {
  85. return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & PAGE_ADDR_MASK);
  86. }
  87. void* GetPage();
  88. void ReturnPage(void* addr) noexcept;
  89. void Swap(TAlignedPagePoolImpl& other) {
  90. DoSwap(FreePages, other.FreePages);
  91. DoSwap(AllPages, other.AllPages);
  92. DoSwap(ActiveBlocks, other.ActiveBlocks);
  93. DoSwap(TotalAllocated, other.TotalAllocated);
  94. DoSwap(PeakAllocated, other.PeakAllocated);
  95. DoSwap(PeakUsed, other.PeakUsed);
  96. DoSwap(Limit, other.Limit);
  97. DoSwap(AllocCount, other.AllocCount);
  98. DoSwap(PageAllocCount, other.PageAllocCount);
  99. DoSwap(PageHitCount, other.PageHitCount);
  100. DoSwap(PageGlobalHitCount, other.PageGlobalHitCount);
  101. DoSwap(PageMissCount, other.PageMissCount);
  102. DoSwap(OffloadedAllocCount, other.OffloadedAllocCount);
  103. DoSwap(OffloadedBytes, other.OffloadedBytes);
  104. DoSwap(OffloadedActiveBytes, other.OffloadedActiveBytes);
  105. DoSwap(Counters, other.Counters);
  106. DoSwap(CheckLostMem, other.CheckLostMem);
  107. DoSwap(AllocNotifyCallback, other.AllocNotifyCallback);
  108. DoSwap(IncreaseMemoryLimitCallback, other.IncreaseMemoryLimitCallback);
  109. }
  110. void PrintStat(size_t usedPages, IOutputStream& out) const;
  111. TString GetDebugInfo() const {
  112. return ToString(DebugInfo);
  113. }
  114. void* GetBlock(size_t size);
  115. void ReturnBlock(void* ptr, size_t size) noexcept;
  116. size_t GetPeakAllocated() const noexcept {
  117. return PeakAllocated;
  118. }
  119. size_t GetPeakUsed() const noexcept {
  120. return PeakUsed;
  121. }
  122. ui64 GetAllocCount() const noexcept {
  123. return AllocCount;
  124. }
  125. ui64 GetPageAllocCount() const noexcept {
  126. return PageAllocCount;
  127. }
  128. ui64 GetPageHitCount() const noexcept {
  129. return PageHitCount;
  130. }
  131. ui64 GetPageGlobalHitCount() const noexcept {
  132. return PageGlobalHitCount;
  133. }
  134. ui64 GetPageMissCount() const noexcept {
  135. return PageMissCount;
  136. }
  137. ui64 GetOffloadedAllocCount() const noexcept {
  138. return OffloadedAllocCount;
  139. }
  140. ui64 GetOffloadedBytes() const noexcept {
  141. return OffloadedBytes;
  142. }
  143. void OffloadAlloc(ui64 size);
  144. void OffloadFree(ui64 size) noexcept;
  145. static void DoCleanupGlobalFreeList(ui64 targetSize = 4 * 64 * 1024 * 1024);
  146. static ui64 GetGlobalPagePoolSize();
  147. ui64 GetLimit() const noexcept {
  148. return Limit;
  149. }
  150. void SetLimit(size_t limit) noexcept {
  151. Limit = limit;
  152. }
  153. void ReleaseFreePages();
  154. void DisableStrictAllocationCheck() noexcept {
  155. CheckLostMem = false;
  156. }
  157. using TAllocNotifyCallback = std::function<void()>;
  158. void SetAllocNotifyCallback(TAllocNotifyCallback&& callback, ui64 notifyBytes = 0) {
  159. AllocNotifyCallback = std::move(callback);
  160. AllocNotifyBytes = notifyBytes;
  161. AllocNotifyCurrentBytes = 0;
  162. }
  163. using TIncreaseMemoryLimitCallback = std::function<void(ui64 currentLimit, ui64 required)>;
  164. void SetIncreaseMemoryLimitCallback(TIncreaseMemoryLimitCallback&& callback) {
  165. IncreaseMemoryLimitCallback = std::move(callback);
  166. }
  167. static void ResetGlobalsUT();
  168. void SetMaximumLimitValueReached(bool isReached) noexcept {
  169. IsMaximumLimitValueReached = isReached;
  170. }
  171. bool GetMaximumLimitValueReached() const noexcept {
  172. return IsMaximumLimitValueReached;
  173. }
  174. bool IsMemoryYellowZoneEnabled() const noexcept {
  175. return IsMemoryYellowZoneReached;
  176. }
  177. void ForcefullySetMemoryYellowZone(bool isEnabled) noexcept {
  178. IsMemoryYellowZoneReached = isEnabled;
  179. IsMemoryYellowZoneForcefullyChanged = true;
  180. }
  181. protected:
  182. void* Alloc(size_t size);
  183. void Free(void* ptr, size_t size) noexcept;
  184. void UpdatePeaks() {
  185. PeakAllocated = Max(PeakAllocated, GetAllocated());
  186. PeakUsed = Max(PeakUsed, GetUsed());
  187. UpdateMemoryYellowZone();
  188. }
  189. void UpdateMemoryYellowZone();
  190. bool TryIncreaseLimit(ui64 required);
  191. protected:
  192. std::stack<void*, std::vector<void*>> FreePages;
  193. std::unordered_set<void*> AllPages;
  194. std::unordered_map<void*, size_t> ActiveBlocks;
  195. size_t TotalAllocated = 0;
  196. size_t PeakAllocated = 0;
  197. size_t PeakUsed = 0;
  198. size_t Limit = 0;
  199. ui64 AllocCount = 0;
  200. ui64 PageAllocCount = 0;
  201. ui64 PageHitCount = 0;
  202. ui64 PageGlobalHitCount = 0;
  203. ui64 PageMissCount = 0;
  204. ui64 OffloadedAllocCount = 0;
  205. ui64 OffloadedBytes = 0;
  206. ui64 OffloadedActiveBytes = 0;
  207. TAlignedPagePoolCounters Counters;
  208. bool CheckLostMem = true;
  209. TAllocNotifyCallback AllocNotifyCallback;
  210. ui64 AllocNotifyBytes = 0;
  211. ui64 AllocNotifyCurrentBytes = 0;
  212. TIncreaseMemoryLimitCallback IncreaseMemoryLimitCallback;
  213. const TSourceLocation DebugInfo;
  214. // Indicates when memory limit is almost reached.
  215. bool IsMemoryYellowZoneReached = false;
  216. // Indicates that memory yellow zone was enabled or disabled forcefully.
  217. // If the value of this variable is true, then the limits specified below will not be applied and
  218. // changing the value can only be done manually.
  219. bool IsMemoryYellowZoneForcefullyChanged = false;
  220. // This theshold is used to determine is memory limit is almost reached.
  221. // If TIncreaseMemoryLimitCallback is set this thresholds should be ignored.
  222. // The yellow zone turns on when memory consumption reaches 80% and turns off when consumption drops below 50%.
  223. const ui8 EnableMemoryYellowZoneThreshold = 80;
  224. const ui8 DisableMemoryYellowZoneThreshold = 50;
  225. // This flag indicates that value of memory limit reached it's maximum.
  226. // Next TryIncreaseLimit call most likely will return false.
  227. bool IsMaximumLimitValueReached = false;
  228. };
  229. using TAlignedPagePool = TAlignedPagePoolImpl<>;
  230. template<typename TMmap = TSystemMmap>
  231. void* GetAlignedPage(ui64 size);
  232. template<typename TMmap = TSystemMmap>
  233. void ReleaseAlignedPage(void* mem, ui64 size);
  234. template<typename TMmap = TSystemMmap>
  235. i64 GetTotalMmapedBytes();
  236. template<typename TMmap = TSystemMmap>
  237. i64 GetTotalFreeListBytes();
  238. size_t GetMemoryMapsCount();
  239. } // NKikimr