]>
git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/monitoring/histogram_windowing.cc
1 // Copyright (c) 2013, 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).
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.
10 #include "monitoring/histogram_windowing.h"
11 #include "monitoring/histogram.h"
12 #include "util/cast_util.h"
16 namespace ROCKSDB_NAMESPACE
{
18 HistogramWindowingImpl::HistogramWindowingImpl() {
19 env_
= Env::Default();
20 window_stats_
.reset(new HistogramStat
[static_cast<size_t>(num_windows_
)]);
24 HistogramWindowingImpl::HistogramWindowingImpl(
26 uint64_t micros_per_window
,
27 uint64_t min_num_per_window
) :
28 num_windows_(num_windows
),
29 micros_per_window_(micros_per_window
),
30 min_num_per_window_(min_num_per_window
) {
31 env_
= Env::Default();
32 window_stats_
.reset(new HistogramStat
[static_cast<size_t>(num_windows_
)]);
36 HistogramWindowingImpl::~HistogramWindowingImpl() {
39 void HistogramWindowingImpl::Clear() {
40 std::lock_guard
<std::mutex
> lock(mutex_
);
43 for (size_t i
= 0; i
< num_windows_
; i
++) {
44 window_stats_
[i
].Clear();
46 current_window_
.store(0, std::memory_order_relaxed
);
47 last_swap_time_
.store(env_
->NowMicros(), std::memory_order_relaxed
);
50 bool HistogramWindowingImpl::Empty() const { return stats_
.Empty(); }
52 // This function is designed to be lock free, as it's in the critical path
54 // Each individual value is atomic, it is just that some samples can go
55 // in the older bucket which is tolerable.
56 void HistogramWindowingImpl::Add(uint64_t value
){
59 // Parent (global) member update
62 // Current window update
63 window_stats_
[static_cast<size_t>(current_window())].Add(value
);
66 void HistogramWindowingImpl::Merge(const Histogram
& other
) {
67 if (strcmp(Name(), other
.Name()) == 0) {
68 Merge(*static_cast_with_check
<const HistogramWindowingImpl
>(&other
));
72 void HistogramWindowingImpl::Merge(const HistogramWindowingImpl
& other
) {
73 std::lock_guard
<std::mutex
> lock(mutex_
);
74 stats_
.Merge(other
.stats_
);
76 if (stats_
.num_buckets_
!= other
.stats_
.num_buckets_
||
77 micros_per_window_
!= other
.micros_per_window_
) {
81 uint64_t cur_window
= current_window();
82 uint64_t other_cur_window
= other
.current_window();
83 // going backwards for alignment
84 for (unsigned int i
= 0;
85 i
< std::min(num_windows_
, other
.num_windows_
); i
++) {
86 uint64_t window_index
=
87 (cur_window
+ num_windows_
- i
) % num_windows_
;
88 uint64_t other_window_index
=
89 (other_cur_window
+ other
.num_windows_
- i
) % other
.num_windows_
;
90 size_t windex
= static_cast<size_t>(window_index
);
91 size_t other_windex
= static_cast<size_t>(other_window_index
);
93 window_stats_
[windex
].Merge(
94 other
.window_stats_
[other_windex
]);
98 std::string
HistogramWindowingImpl::ToString() const {
99 return stats_
.ToString();
102 double HistogramWindowingImpl::Median() const {
103 return Percentile(50.0);
106 double HistogramWindowingImpl::Percentile(double p
) const {
107 // Retry 3 times in total
108 for (int retry
= 0; retry
< 3; retry
++) {
109 uint64_t start_num
= stats_
.num();
110 double result
= stats_
.Percentile(p
);
111 // Detect if swap buckets or Clear() was called during calculation
112 if (stats_
.num() >= start_num
) {
119 double HistogramWindowingImpl::Average() const {
120 return stats_
.Average();
123 double HistogramWindowingImpl::StandardDeviation() const {
124 return stats_
.StandardDeviation();
127 void HistogramWindowingImpl::Data(HistogramData
* const data
) const {
131 void HistogramWindowingImpl::TimerTick() {
132 uint64_t curr_time
= env_
->NowMicros();
133 size_t curr_window_
= static_cast<size_t>(current_window());
134 if (curr_time
- last_swap_time() > micros_per_window_
&&
135 window_stats_
[curr_window_
].num() >= min_num_per_window_
) {
140 void HistogramWindowingImpl::SwapHistoryBucket() {
141 // Threads executing Add() would be competing for this mutex, the first one
142 // who got the metex would take care of the bucket swap, other threads
144 // If mutex is held by Merge() or Clear(), next Add() will take care of the
146 if (mutex_
.try_lock()) {
147 last_swap_time_
.store(env_
->NowMicros(), std::memory_order_relaxed
);
149 uint64_t curr_window
= current_window();
150 uint64_t next_window
= (curr_window
== num_windows_
- 1) ?
153 // subtract next buckets from totals and swap to next buckets
154 HistogramStat
& stats_to_drop
=
155 window_stats_
[static_cast<size_t>(next_window
)];
157 if (!stats_to_drop
.Empty()) {
158 for (size_t b
= 0; b
< stats_
.num_buckets_
; b
++){
159 stats_
.buckets_
[b
].fetch_sub(
160 stats_to_drop
.bucket_at(b
), std::memory_order_relaxed
);
163 if (stats_
.min() == stats_to_drop
.min()) {
164 uint64_t new_min
= std::numeric_limits
<uint64_t>::max();
165 for (unsigned int i
= 0; i
< num_windows_
; i
++) {
166 if (i
!= next_window
) {
167 uint64_t m
= window_stats_
[i
].min();
168 if (m
< new_min
) new_min
= m
;
171 stats_
.min_
.store(new_min
, std::memory_order_relaxed
);
174 if (stats_
.max() == stats_to_drop
.max()) {
175 uint64_t new_max
= 0;
176 for (unsigned int i
= 0; i
< num_windows_
; i
++) {
177 if (i
!= next_window
) {
178 uint64_t m
= window_stats_
[i
].max();
179 if (m
> new_max
) new_max
= m
;
182 stats_
.max_
.store(new_max
, std::memory_order_relaxed
);
185 stats_
.num_
.fetch_sub(stats_to_drop
.num(), std::memory_order_relaxed
);
186 stats_
.sum_
.fetch_sub(stats_to_drop
.sum(), std::memory_order_relaxed
);
187 stats_
.sum_squares_
.fetch_sub(
188 stats_to_drop
.sum_squares(), std::memory_order_relaxed
);
190 stats_to_drop
.Clear();
193 // advance to next window bucket
194 current_window_
.store(next_window
, std::memory_order_relaxed
);
200 } // namespace ROCKSDB_NAMESPACE