percentile_lg.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #pragma once
  2. #include <library/cpp/containers/stack_vector/stack_vec.h>
  3. #include <util/generic/bitops.h>
  4. #include <cmath>
  5. #include "percentile_base.h"
  6. namespace NMonitoring {
  7. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  8. // Percentile tracker for monitoring
  9. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  10. template <size_t BASE_BITS, size_t EXP_BITS, size_t FRAME_COUNT>
  11. struct TPercentileTrackerLg : public TPercentileBase {
  12. static constexpr size_t BUCKET_COUNT = size_t(1) << EXP_BITS;
  13. static constexpr size_t BUCKET_SIZE = size_t(1) << BASE_BITS;
  14. static constexpr size_t ITEMS_COUNT = BUCKET_COUNT * BUCKET_SIZE;
  15. static constexpr size_t TRACKER_LIMIT = BUCKET_SIZE * ((size_t(1) << BUCKET_COUNT) - 1)
  16. - (size_t(1) << (BUCKET_COUNT - 1));
  17. static constexpr size_t MAX_GRANULARITY = size_t(1) << (BUCKET_COUNT - 1);
  18. size_t Borders[BUCKET_COUNT];
  19. TAtomic Items[ITEMS_COUNT];
  20. TAtomicBase Frame[FRAME_COUNT][ITEMS_COUNT];
  21. size_t CurrentFrame;
  22. TPercentileTrackerLg()
  23. : CurrentFrame(0)
  24. {
  25. Borders[0] = 0;
  26. for (size_t i = 1; i < BUCKET_COUNT; ++i) {
  27. Borders[i] = Borders[i-1] + (BUCKET_SIZE << (i - 1));
  28. }
  29. for (size_t i = 0; i < ITEMS_COUNT; ++i) {
  30. AtomicSet(Items[i], 0);
  31. }
  32. for (size_t frame = 0; frame < FRAME_COUNT; ++frame) {
  33. for (size_t bucket = 0; bucket < ITEMS_COUNT; ++bucket) {
  34. Frame[frame][bucket] = 0;
  35. }
  36. }
  37. }
  38. size_t inline BucketIdxIf(size_t value) {
  39. static_assert(BASE_BITS == 5, "if-based bucket calculation cannot be used if BASE_BITS != 5");
  40. size_t bucket_idx;
  41. if (value < 8160) {
  42. if (value < 480) {
  43. if (value < 96) {
  44. if (value < 32) {
  45. bucket_idx = 0;
  46. } else {
  47. bucket_idx = 1;
  48. }
  49. } else {
  50. if (value < 224) {
  51. bucket_idx = 2;
  52. } else {
  53. bucket_idx = 3;
  54. }
  55. }
  56. } else {
  57. if (value < 2016) {
  58. if (value < 992) {
  59. bucket_idx = 4;
  60. } else {
  61. bucket_idx = 5;
  62. }
  63. } else {
  64. if (value < 4064) {
  65. bucket_idx = 6;
  66. } else {
  67. bucket_idx = 7;
  68. }
  69. }
  70. }
  71. } else {
  72. if (value < 131040) {
  73. if (value < 32736) {
  74. if (value < 16352) {
  75. bucket_idx = 8;
  76. } else {
  77. bucket_idx = 9;
  78. }
  79. } else {
  80. if (value < 65504) {
  81. bucket_idx = 10;
  82. } else {
  83. bucket_idx = 11;
  84. }
  85. }
  86. } else {
  87. if (value < 524256) {
  88. if (value < 262112) {
  89. bucket_idx = 12;
  90. } else {
  91. bucket_idx = 13;
  92. }
  93. } else {
  94. if (value < 1048544) {
  95. bucket_idx = 14;
  96. } else {
  97. bucket_idx = 15;
  98. }
  99. }
  100. }
  101. }
  102. return Min(bucket_idx, BUCKET_COUNT - 1);
  103. }
  104. size_t inline BucketIdxBinarySearch(size_t value) {
  105. size_t l = 0;
  106. size_t r = BUCKET_COUNT;
  107. while (l < r - 1) {
  108. size_t mid = (l + r) / 2;
  109. if (value < Borders[mid]) {
  110. r = mid;
  111. } else {
  112. l = mid;
  113. }
  114. }
  115. return l;
  116. }
  117. size_t inline BucketIdxMostSignificantBit(size_t value) {
  118. size_t bucket_idx = MostSignificantBit(value + BUCKET_SIZE) - BASE_BITS;
  119. return Min(bucket_idx, BUCKET_COUNT - 1);
  120. }
  121. void Increment(size_t value) {
  122. size_t bucket_idx = BucketIdxMostSignificantBit(value);
  123. size_t inside_bucket_idx = (value - Borders[bucket_idx] + (1 << bucket_idx) - 1) >> bucket_idx;
  124. size_t idx = bucket_idx * BUCKET_SIZE + inside_bucket_idx;
  125. AtomicIncrement(Items[Min(idx, ITEMS_COUNT - 1)]);
  126. }
  127. // Needed only for tests
  128. size_t GetPercentile(float threshold) {
  129. TStackVec<TAtomicBase, ITEMS_COUNT> totals(ITEMS_COUNT);
  130. TAtomicBase total = 0;
  131. for (size_t i = 0; i < ITEMS_COUNT; ++i) {
  132. total += AtomicGet(Items[i]);
  133. totals[i] = total;
  134. }
  135. TAtomicBase item_threshold = std::llround(threshold * (float)total);
  136. item_threshold = Min(item_threshold, total);
  137. auto it = LowerBound(totals.begin(), totals.end(), item_threshold);
  138. size_t index = it - totals.begin();
  139. size_t bucket_idx = index / BUCKET_SIZE;
  140. return Borders[bucket_idx] + ((index % BUCKET_SIZE) << bucket_idx);
  141. }
  142. // shift frame (call periodically)
  143. void Update() {
  144. TStackVec<TAtomicBase, ITEMS_COUNT> totals(ITEMS_COUNT);
  145. TAtomicBase total = 0;
  146. for (size_t i = 0; i < ITEMS_COUNT; ++i) {
  147. TAtomicBase item = AtomicGet(Items[i]);
  148. TAtomicBase prevItem = Frame[CurrentFrame][i];
  149. Frame[CurrentFrame][i] = item;
  150. total += item - prevItem;
  151. totals[i] = total;
  152. }
  153. for (size_t i = 0; i < Percentiles.size(); ++i) {
  154. TPercentile &percentile = Percentiles[i];
  155. TAtomicBase threshold = std::llround(percentile.first * (float)total);
  156. threshold = Min(threshold, total);
  157. auto it = LowerBound(totals.begin(), totals.end(), threshold);
  158. size_t index = it - totals.begin();
  159. size_t bucket_idx = index / BUCKET_SIZE;
  160. (*percentile.second) = Borders[bucket_idx] + ((index % BUCKET_SIZE) << bucket_idx);
  161. }
  162. CurrentFrame = (CurrentFrame + 1) % FRAME_COUNT;
  163. }
  164. };
  165. } // NMonitoring