config.h 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #pragma once
  2. #include "defs.h"
  3. #include <library/cpp/actors/util/cpumask.h>
  4. #include <library/cpp/monlib/dynamic_counters/counters.h>
  5. #include <util/datetime/base.h>
  6. #include <util/generic/ptr.h>
  7. #include <util/generic/string.h>
  8. #include <util/generic/vector.h>
  9. namespace NActors {
  10. struct TBalancingConfig {
  11. // Default cpu count (used during overload). Zero value disables this pool balancing
  12. // 1) Sum of `Cpus` on all pools cannot be changed without restart
  13. // (changing cpu mode between Shared and Assigned is not implemented yet)
  14. // 2) This sum must be equal to TUnitedWorkersConfig::CpuCount,
  15. // otherwise `CpuCount - SUM(Cpus)` cpus will be in Shared mode (i.e. actorsystem 2.0)
  16. ui32 Cpus = 0;
  17. ui32 MinCpus = 0; // Lower balancing bound, should be at least 1, and not greater than `Cpus`
  18. ui32 MaxCpus = 0; // Higher balancing bound, should be not lower than `Cpus`
  19. ui8 Priority = 0; // Priority of pool to obtain cpu due to balancing (higher is better)
  20. ui64 ToleratedLatencyUs = 0; // p100-latency threshold indicating that more cpus are required by pool
  21. };
  22. struct TBalancerConfig {
  23. ui64 PeriodUs = 15000000; // Time between balancer steps
  24. };
  25. struct TBasicExecutorPoolConfig {
  26. static constexpr TDuration DEFAULT_TIME_PER_MAILBOX = TDuration::MilliSeconds(10);
  27. static constexpr ui32 DEFAULT_EVENTS_PER_MAILBOX = 100;
  28. ui32 PoolId = 0;
  29. TString PoolName;
  30. ui32 Threads = 1;
  31. ui64 SpinThreshold = 100;
  32. TCpuMask Affinity; // Executor thread affinity
  33. TDuration TimePerMailbox = DEFAULT_TIME_PER_MAILBOX;
  34. ui32 EventsPerMailbox = DEFAULT_EVENTS_PER_MAILBOX;
  35. int RealtimePriority = 0;
  36. i16 MinThreadCount = 0;
  37. i16 MaxThreadCount = 0;
  38. i16 DefaultThreadCount = 0;
  39. i16 Priority = 0;
  40. i16 SharedExecutorsCount = 0;
  41. i16 SoftProcessingDurationTs = 0;
  42. };
  43. struct TIOExecutorPoolConfig {
  44. ui32 PoolId = 0;
  45. TString PoolName;
  46. ui32 Threads = 1;
  47. TCpuMask Affinity; // Executor thread affinity
  48. };
  49. struct TUnitedExecutorPoolConfig {
  50. static constexpr TDuration DEFAULT_TIME_PER_MAILBOX = TDuration::MilliSeconds(10);
  51. static constexpr ui32 DEFAULT_EVENTS_PER_MAILBOX = 100;
  52. ui32 PoolId = 0;
  53. TString PoolName;
  54. // Resource sharing
  55. ui32 Concurrency = 0; // Limits simultaneously running mailboxes count if set to non-zero value (do not set if Balancing.Cpus != 0)
  56. TPoolWeight Weight = 0; // Weight in fair cpu-local pool scheduler
  57. TCpuMask Allowed; // Allowed CPUs for workers to run this pool on (ignored if balancer works, i.e. actorsystem 1.5)
  58. // Single mailbox execution limits
  59. TDuration TimePerMailbox = DEFAULT_TIME_PER_MAILBOX;
  60. ui32 EventsPerMailbox = DEFAULT_EVENTS_PER_MAILBOX;
  61. // Long-term balancing
  62. TBalancingConfig Balancing;
  63. };
  64. struct TUnitedWorkersConfig {
  65. ui32 CpuCount = 0; // Total CPUs running united workers (i.e. TBasicExecutorPoolConfig::Threads analog); set to zero to disable united workers
  66. ui64 SpinThresholdUs = 100; // Limit for active spinning in case all pools became idle
  67. ui64 PoolLimitUs = 500; // Soft limit on pool execution
  68. ui64 EventLimitUs = 100; // Hard limit on last event execution exceeding pool limit
  69. ui64 LimitPrecisionUs = 100; // Maximum delay of timer on limit excess (delay needed to avoid settimer syscall on every pool switch)
  70. ui64 FastWorkerPriority = 10; // Real-time priority of workers not exceeding hard limits
  71. ui64 IdleWorkerPriority = 20; // Real-time priority of standby workers waiting for hard preemption on timers (should be greater than FastWorkerPriority)
  72. TCpuMask Allowed; // Allowed CPUs for workers to run on (every worker has affinity for exactly one cpu)
  73. bool NoRealtime = false; // For environments w/o permissions for RT-threads
  74. bool NoAffinity = false; // For environments w/o permissions for cpu affinity
  75. TBalancerConfig Balancer;
  76. };
  77. struct TSelfPingInfo {
  78. NMonitoring::TDynamicCounters::TCounterPtr AvgPingCounter;
  79. NMonitoring::TDynamicCounters::TCounterPtr AvgPingCounterWithSmallWindow;
  80. ui32 MaxAvgPingUs;
  81. };
  82. struct TCpuManagerConfig {
  83. TUnitedWorkersConfig UnitedWorkers;
  84. TVector<TBasicExecutorPoolConfig> Basic;
  85. TVector<TIOExecutorPoolConfig> IO;
  86. TVector<TUnitedExecutorPoolConfig> United;
  87. TVector<TSelfPingInfo> PingInfoByPool;
  88. ui32 GetExecutorsCount() const {
  89. return Basic.size() + IO.size() + United.size();
  90. }
  91. TString GetPoolName(ui32 poolId) const {
  92. for (const auto& p : Basic) {
  93. if (p.PoolId == poolId) {
  94. return p.PoolName;
  95. }
  96. }
  97. for (const auto& p : IO) {
  98. if (p.PoolId == poolId) {
  99. return p.PoolName;
  100. }
  101. }
  102. for (const auto& p : United) {
  103. if (p.PoolId == poolId) {
  104. return p.PoolName;
  105. }
  106. }
  107. Y_FAIL("undefined pool id: %" PRIu32, (ui32)poolId);
  108. }
  109. std::optional<ui32> GetThreadsOptional(ui32 poolId) const {
  110. for (const auto& p : Basic) {
  111. if (p.PoolId == poolId) {
  112. return p.DefaultThreadCount;
  113. }
  114. }
  115. for (const auto& p : IO) {
  116. if (p.PoolId == poolId) {
  117. return p.Threads;
  118. }
  119. }
  120. for (const auto& p : United) {
  121. if (p.PoolId == poolId) {
  122. return p.Concurrency ? p.Concurrency : UnitedWorkers.CpuCount;
  123. }
  124. }
  125. return {};
  126. }
  127. ui32 GetThreads(ui32 poolId) const {
  128. auto result = GetThreadsOptional(poolId);
  129. Y_VERIFY(result, "undefined pool id: %" PRIu32, (ui32)poolId);
  130. return *result;
  131. }
  132. };
  133. struct TSchedulerConfig {
  134. TSchedulerConfig(
  135. ui64 resolution = 1024,
  136. ui64 spinThreshold = 100,
  137. ui64 progress = 10000,
  138. bool useSchedulerActor = false)
  139. : ResolutionMicroseconds(resolution)
  140. , SpinThreshold(spinThreshold)
  141. , ProgressThreshold(progress)
  142. , UseSchedulerActor(useSchedulerActor)
  143. {}
  144. ui64 ResolutionMicroseconds = 1024;
  145. ui64 SpinThreshold = 100;
  146. ui64 ProgressThreshold = 10000;
  147. bool UseSchedulerActor = false; // False is default because tests use scheduler thread
  148. ui64 RelaxedSendPaceEventsPerSecond = 200000;
  149. ui64 RelaxedSendPaceEventsPerCycle = RelaxedSendPaceEventsPerSecond * ResolutionMicroseconds / 1000000;
  150. // For resolution >= 250000 microseconds threshold is SendPace
  151. // For resolution <= 250 microseconds threshold is 20 * SendPace
  152. ui64 RelaxedSendThresholdEventsPerSecond = RelaxedSendPaceEventsPerSecond *
  153. (20 - ((20 - 1) * ClampVal(ResolutionMicroseconds, ui64(250), ui64(250000)) - 250) / (250000 - 250));
  154. ui64 RelaxedSendThresholdEventsPerCycle = RelaxedSendThresholdEventsPerSecond * ResolutionMicroseconds / 1000000;
  155. // Optional subsection for scheduler counters (usually subsystem=utils)
  156. NMonitoring::TDynamicCounterPtr MonCounters = nullptr;
  157. };
  158. struct TCpuAllocation {
  159. struct TPoolAllocation {
  160. TPoolId PoolId;
  161. TPoolWeight Weight;
  162. TPoolAllocation(TPoolId poolId = 0, TPoolWeight weight = 0)
  163. : PoolId(poolId)
  164. , Weight(weight)
  165. {}
  166. };
  167. TCpuId CpuId;
  168. TVector<TPoolAllocation> AllowedPools;
  169. TPoolsMask GetPoolsMask() const {
  170. TPoolsMask mask = 0;
  171. for (const auto& pa : AllowedPools) {
  172. if (pa.PoolId < MaxPools) {
  173. mask &= (1ull << pa.PoolId);
  174. }
  175. }
  176. return mask;
  177. }
  178. bool HasPool(TPoolId pool) const {
  179. for (const auto& pa : AllowedPools) {
  180. if (pa.PoolId == pool) {
  181. return true;
  182. }
  183. }
  184. return false;
  185. }
  186. };
  187. struct TCpuAllocationConfig {
  188. TVector<TCpuAllocation> Items;
  189. TCpuAllocationConfig(const TCpuMask& available, const TCpuManagerConfig& cfg) {
  190. for (const TUnitedExecutorPoolConfig& pool : cfg.United) {
  191. Y_VERIFY(pool.PoolId < MaxPools, "wrong PoolId of united executor pool: %s(%d)",
  192. pool.PoolName.c_str(), (pool.PoolId));
  193. }
  194. ui32 allocated[MaxPools] = {0};
  195. for (TCpuId cpu = 0; cpu < available.Size() && Items.size() < cfg.UnitedWorkers.CpuCount; cpu++) {
  196. if (available.IsSet(cpu)) {
  197. TCpuAllocation item;
  198. item.CpuId = cpu;
  199. for (const TUnitedExecutorPoolConfig& pool : cfg.United) {
  200. if (cfg.UnitedWorkers.Allowed.IsEmpty() || cfg.UnitedWorkers.Allowed.IsSet(cpu)) {
  201. if (pool.Allowed.IsEmpty() || pool.Allowed.IsSet(cpu)) {
  202. item.AllowedPools.emplace_back(pool.PoolId, pool.Weight);
  203. allocated[pool.PoolId]++;
  204. }
  205. }
  206. }
  207. if (!item.AllowedPools.empty()) {
  208. Items.push_back(item);
  209. }
  210. }
  211. }
  212. for (const TUnitedExecutorPoolConfig& pool : cfg.United) {
  213. Y_VERIFY(allocated[pool.PoolId] > 0, "unable to allocate cpu for united executor pool: %s(%d)",
  214. pool.PoolName.c_str(), (pool.PoolId));
  215. }
  216. }
  217. operator bool() const {
  218. return !Items.empty();
  219. }
  220. };
  221. }