aligned_page_pool.h 9.7 KB

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