histogram.cc 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  7. // Use of this source code is governed by a BSD-style license that can be
  8. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  9. #include "monitoring/histogram.h"
  10. #include <stdio.h>
  11. #include <cassert>
  12. #include <cinttypes>
  13. #include <cmath>
  14. #include "port/port.h"
  15. #include "util/cast_util.h"
  16. namespace ROCKSDB_NAMESPACE {
  17. HistogramBucketMapper::HistogramBucketMapper() {
  18. // If you change this, you also need to change
  19. // size of array buckets_ in HistogramImpl
  20. bucketValues_ = {1, 2};
  21. valueIndexMap_ = {{1, 0}, {2, 1}};
  22. double bucket_val = static_cast<double>(bucketValues_.back());
  23. while ((bucket_val = 1.5 * bucket_val) <= static_cast<double>(port::kMaxUint64)) {
  24. bucketValues_.push_back(static_cast<uint64_t>(bucket_val));
  25. // Extracts two most significant digits to make histogram buckets more
  26. // human-readable. E.g., 172 becomes 170.
  27. uint64_t pow_of_ten = 1;
  28. while (bucketValues_.back() / 10 > 10) {
  29. bucketValues_.back() /= 10;
  30. pow_of_ten *= 10;
  31. }
  32. bucketValues_.back() *= pow_of_ten;
  33. valueIndexMap_[bucketValues_.back()] = bucketValues_.size() - 1;
  34. }
  35. maxBucketValue_ = bucketValues_.back();
  36. minBucketValue_ = bucketValues_.front();
  37. }
  38. size_t HistogramBucketMapper::IndexForValue(const uint64_t value) const {
  39. if (value >= maxBucketValue_) {
  40. return bucketValues_.size() - 1;
  41. } else if ( value >= minBucketValue_ ) {
  42. std::map<uint64_t, uint64_t>::const_iterator lowerBound =
  43. valueIndexMap_.lower_bound(value);
  44. if (lowerBound != valueIndexMap_.end()) {
  45. return static_cast<size_t>(lowerBound->second);
  46. } else {
  47. return 0;
  48. }
  49. } else {
  50. return 0;
  51. }
  52. }
  53. namespace {
  54. const HistogramBucketMapper bucketMapper;
  55. }
  56. HistogramStat::HistogramStat()
  57. : num_buckets_(bucketMapper.BucketCount()) {
  58. assert(num_buckets_ == sizeof(buckets_) / sizeof(*buckets_));
  59. Clear();
  60. }
  61. void HistogramStat::Clear() {
  62. min_.store(bucketMapper.LastValue(), std::memory_order_relaxed);
  63. max_.store(0, std::memory_order_relaxed);
  64. num_.store(0, std::memory_order_relaxed);
  65. sum_.store(0, std::memory_order_relaxed);
  66. sum_squares_.store(0, std::memory_order_relaxed);
  67. for (unsigned int b = 0; b < num_buckets_; b++) {
  68. buckets_[b].store(0, std::memory_order_relaxed);
  69. }
  70. };
  71. bool HistogramStat::Empty() const { return num() == 0; }
  72. void HistogramStat::Add(uint64_t value) {
  73. // This function is designed to be lock free, as it's in the critical path
  74. // of any operation. Each individual value is atomic and the order of updates
  75. // by concurrent threads is tolerable.
  76. const size_t index = bucketMapper.IndexForValue(value);
  77. assert(index < num_buckets_);
  78. buckets_[index].store(buckets_[index].load(std::memory_order_relaxed) + 1,
  79. std::memory_order_relaxed);
  80. uint64_t old_min = min();
  81. if (value < old_min) {
  82. min_.store(value, std::memory_order_relaxed);
  83. }
  84. uint64_t old_max = max();
  85. if (value > old_max) {
  86. max_.store(value, std::memory_order_relaxed);
  87. }
  88. num_.store(num_.load(std::memory_order_relaxed) + 1,
  89. std::memory_order_relaxed);
  90. sum_.store(sum_.load(std::memory_order_relaxed) + value,
  91. std::memory_order_relaxed);
  92. sum_squares_.store(
  93. sum_squares_.load(std::memory_order_relaxed) + value * value,
  94. std::memory_order_relaxed);
  95. }
  96. void HistogramStat::Merge(const HistogramStat& other) {
  97. // This function needs to be performned with the outer lock acquired
  98. // However, atomic operation on every member is still need, since Add()
  99. // requires no lock and value update can still happen concurrently
  100. uint64_t old_min = min();
  101. uint64_t other_min = other.min();
  102. while (other_min < old_min &&
  103. !min_.compare_exchange_weak(old_min, other_min)) {}
  104. uint64_t old_max = max();
  105. uint64_t other_max = other.max();
  106. while (other_max > old_max &&
  107. !max_.compare_exchange_weak(old_max, other_max)) {}
  108. num_.fetch_add(other.num(), std::memory_order_relaxed);
  109. sum_.fetch_add(other.sum(), std::memory_order_relaxed);
  110. sum_squares_.fetch_add(other.sum_squares(), std::memory_order_relaxed);
  111. for (unsigned int b = 0; b < num_buckets_; b++) {
  112. buckets_[b].fetch_add(other.bucket_at(b), std::memory_order_relaxed);
  113. }
  114. }
  115. double HistogramStat::Median() const {
  116. return Percentile(50.0);
  117. }
  118. double HistogramStat::Percentile(double p) const {
  119. double threshold = num() * (p / 100.0);
  120. uint64_t cumulative_sum = 0;
  121. for (unsigned int b = 0; b < num_buckets_; b++) {
  122. uint64_t bucket_value = bucket_at(b);
  123. cumulative_sum += bucket_value;
  124. if (cumulative_sum >= threshold) {
  125. // Scale linearly within this bucket
  126. uint64_t left_point = (b == 0) ? 0 : bucketMapper.BucketLimit(b-1);
  127. uint64_t right_point = bucketMapper.BucketLimit(b);
  128. uint64_t left_sum = cumulative_sum - bucket_value;
  129. uint64_t right_sum = cumulative_sum;
  130. double pos = 0;
  131. uint64_t right_left_diff = right_sum - left_sum;
  132. if (right_left_diff != 0) {
  133. pos = (threshold - left_sum) / right_left_diff;
  134. }
  135. double r = left_point + (right_point - left_point) * pos;
  136. uint64_t cur_min = min();
  137. uint64_t cur_max = max();
  138. if (r < cur_min) r = static_cast<double>(cur_min);
  139. if (r > cur_max) r = static_cast<double>(cur_max);
  140. return r;
  141. }
  142. }
  143. return static_cast<double>(max());
  144. }
  145. double HistogramStat::Average() const {
  146. uint64_t cur_num = num();
  147. uint64_t cur_sum = sum();
  148. if (cur_num == 0) return 0;
  149. return static_cast<double>(cur_sum) / static_cast<double>(cur_num);
  150. }
  151. double HistogramStat::StandardDeviation() const {
  152. uint64_t cur_num = num();
  153. uint64_t cur_sum = sum();
  154. uint64_t cur_sum_squares = sum_squares();
  155. if (cur_num == 0) return 0;
  156. double variance =
  157. static_cast<double>(cur_sum_squares * cur_num - cur_sum * cur_sum) /
  158. static_cast<double>(cur_num * cur_num);
  159. return std::sqrt(variance);
  160. }
  161. std::string HistogramStat::ToString() const {
  162. uint64_t cur_num = num();
  163. std::string r;
  164. char buf[1650];
  165. snprintf(buf, sizeof(buf),
  166. "Count: %" PRIu64 " Average: %.4f StdDev: %.2f\n",
  167. cur_num, Average(), StandardDeviation());
  168. r.append(buf);
  169. snprintf(buf, sizeof(buf),
  170. "Min: %" PRIu64 " Median: %.4f Max: %" PRIu64 "\n",
  171. (cur_num == 0 ? 0 : min()), Median(), (cur_num == 0 ? 0 : max()));
  172. r.append(buf);
  173. snprintf(buf, sizeof(buf),
  174. "Percentiles: "
  175. "P50: %.2f P75: %.2f P99: %.2f P99.9: %.2f P99.99: %.2f\n",
  176. Percentile(50), Percentile(75), Percentile(99), Percentile(99.9),
  177. Percentile(99.99));
  178. r.append(buf);
  179. r.append("------------------------------------------------------\n");
  180. if (cur_num == 0) return r; // all buckets are empty
  181. const double mult = 100.0 / cur_num;
  182. uint64_t cumulative_sum = 0;
  183. for (unsigned int b = 0; b < num_buckets_; b++) {
  184. uint64_t bucket_value = bucket_at(b);
  185. if (bucket_value <= 0.0) continue;
  186. cumulative_sum += bucket_value;
  187. snprintf(buf, sizeof(buf),
  188. "%c %7" PRIu64 ", %7" PRIu64 " ] %8" PRIu64 " %7.3f%% %7.3f%% ",
  189. (b == 0) ? '[' : '(',
  190. (b == 0) ? 0 : bucketMapper.BucketLimit(b-1), // left
  191. bucketMapper.BucketLimit(b), // right
  192. bucket_value, // count
  193. (mult * bucket_value), // percentage
  194. (mult * cumulative_sum)); // cumulative percentage
  195. r.append(buf);
  196. // Add hash marks based on percentage; 20 marks for 100%.
  197. size_t marks = static_cast<size_t>(mult * bucket_value / 5 + 0.5);
  198. r.append(marks, '#');
  199. r.push_back('\n');
  200. }
  201. return r;
  202. }
  203. void HistogramStat::Data(HistogramData * const data) const {
  204. assert(data);
  205. data->median = Median();
  206. data->percentile95 = Percentile(95);
  207. data->percentile99 = Percentile(99);
  208. data->max = static_cast<double>(max());
  209. data->average = Average();
  210. data->standard_deviation = StandardDeviation();
  211. data->count = num();
  212. data->sum = sum();
  213. data->min = static_cast<double>(min());
  214. }
  215. void HistogramImpl::Clear() {
  216. std::lock_guard<std::mutex> lock(mutex_);
  217. stats_.Clear();
  218. }
  219. bool HistogramImpl::Empty() const {
  220. return stats_.Empty();
  221. }
  222. void HistogramImpl::Add(uint64_t value) {
  223. stats_.Add(value);
  224. }
  225. void HistogramImpl::Merge(const Histogram& other) {
  226. if (strcmp(Name(), other.Name()) == 0) {
  227. Merge(
  228. *static_cast_with_check<const HistogramImpl, const Histogram>(&other));
  229. }
  230. }
  231. void HistogramImpl::Merge(const HistogramImpl& other) {
  232. std::lock_guard<std::mutex> lock(mutex_);
  233. stats_.Merge(other.stats_);
  234. }
  235. double HistogramImpl::Median() const {
  236. return stats_.Median();
  237. }
  238. double HistogramImpl::Percentile(double p) const {
  239. return stats_.Percentile(p);
  240. }
  241. double HistogramImpl::Average() const {
  242. return stats_.Average();
  243. }
  244. double HistogramImpl::StandardDeviation() const {
  245. return stats_.StandardDeviation();
  246. }
  247. std::string HistogramImpl::ToString() const {
  248. return stats_.ToString();
  249. }
  250. void HistogramImpl::Data(HistogramData * const data) const {
  251. stats_.Data(data);
  252. }
  253. } // namespace ROCKSDB_NAMESPACE