/src/brpc/src/bvar/detail/percentile.cpp
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | // Date: 2015/09/15 15:14:32 |
19 | | |
20 | | #include "bvar/detail/percentile.h" |
21 | | #include "butil/logging.h" |
22 | | |
23 | | namespace bvar { |
24 | | namespace detail { |
25 | | #if !WITH_BABYLON_COUNTER |
26 | 0 | inline uint32_t ones32(uint32_t x) { |
27 | | /* 32-bit recursive reduction using SWAR... |
28 | | * but first step is mapping 2-bit values |
29 | | * into sum of 2 1-bit values in sneaky way |
30 | | */ |
31 | 0 | x -= ((x >> 1) & 0x55555555); |
32 | 0 | x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); |
33 | 0 | x = (((x >> 4) + x) & 0x0f0f0f0f); |
34 | 0 | x += (x >> 8); |
35 | 0 | x += (x >> 16); |
36 | 0 | return (x & 0x0000003f); |
37 | 0 | } |
38 | | |
39 | 0 | inline uint32_t log2(uint32_t x) { |
40 | 0 | int y = (x & (x - 1)); |
41 | 0 | y |= -y; |
42 | 0 | y >>= 31; |
43 | 0 | x |= (x >> 1); |
44 | 0 | x |= (x >> 2); |
45 | 0 | x |= (x >> 4); |
46 | 0 | x |= (x >> 8); |
47 | 0 | x |= (x >> 16); |
48 | 0 | return(ones32(x) - 1 - y); |
49 | 0 | } |
50 | | |
51 | 0 | inline size_t get_interval_index(int64_t &x) { |
52 | 0 | if (x <= 2) { |
53 | 0 | return 0; |
54 | 0 | } else if (x > std::numeric_limits<uint32_t>::max()) { |
55 | 0 | x = std::numeric_limits<uint32_t>::max(); |
56 | 0 | return 31; |
57 | 0 | } else { |
58 | 0 | return log2(x) - 1; |
59 | 0 | } |
60 | 0 | } |
61 | | |
62 | | class AddLatency { |
63 | | public: |
64 | 0 | AddLatency(int64_t latency) : _latency(latency) {} |
65 | | |
66 | | void operator()(GlobalValue<Percentile::combiner_type>& global_value, |
67 | 0 | ThreadLocalPercentileSamples& local_value) const { |
68 | | // Copy to latency since get_interval_index may change input. |
69 | 0 | int64_t latency = _latency; |
70 | 0 | const size_t index = get_interval_index(latency); |
71 | 0 | PercentileInterval<ThreadLocalPercentileSamples::SAMPLE_SIZE>& |
72 | 0 | interval = local_value.get_interval_at(index); |
73 | 0 | if (interval.full()) { |
74 | 0 | GlobalPercentileSamples* g = global_value.lock(); |
75 | 0 | g->get_interval_at(index).merge(interval); |
76 | 0 | g->_num_added += interval.added_count(); |
77 | 0 | global_value.unlock(); |
78 | 0 | local_value._num_added -= interval.added_count(); |
79 | 0 | interval.clear(); |
80 | 0 | } |
81 | 0 | interval.add64(latency); |
82 | 0 | ++local_value._num_added; |
83 | 0 | } |
84 | | private: |
85 | | int64_t _latency; |
86 | | }; |
87 | | |
88 | | Percentile::Percentile() |
89 | 0 | : _combiner(std::make_shared<combiner_type>()), _sampler(NULL) {} |
90 | | |
91 | 0 | Percentile::~Percentile() { |
92 | | // Have to destroy sampler first to avoid the race between destruction and |
93 | | // sampler |
94 | 0 | if (_sampler != NULL) { |
95 | 0 | _sampler->destroy(); |
96 | 0 | _sampler = NULL; |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | Percentile::value_type Percentile::reset() { |
101 | 0 | return _combiner->reset_all_agents(); |
102 | 0 | } |
103 | | |
104 | 0 | Percentile::value_type Percentile::get_value() const { |
105 | 0 | return _combiner->combine_agents(); |
106 | 0 | } |
107 | | |
108 | 0 | Percentile &Percentile::operator<<(int64_t latency) { |
109 | 0 | agent_type* agent = _combiner->get_or_create_tls_agent(); |
110 | 0 | if (BAIDU_UNLIKELY(!agent)) { |
111 | 0 | LOG(FATAL) << "Fail to create agent"; |
112 | 0 | return *this; |
113 | 0 | } |
114 | 0 | if (latency < 0) { |
115 | | // we don't check overflow(of uint32) in percentile because the |
116 | | // overflowed value which is included in last range does not affect |
117 | | // overall distribution of other values too much. |
118 | 0 | if (!_debug_name.empty()) { |
119 | 0 | LOG(WARNING) << "Input=" << latency << " to `" << _debug_name |
120 | 0 | << "' is negative, drop"; |
121 | 0 | } else { |
122 | 0 | LOG(WARNING) << "Input=" << latency << " to Percentile(" |
123 | 0 | << (void*)this << ") is negative, drop"; |
124 | 0 | } |
125 | 0 | return *this; |
126 | 0 | } |
127 | 0 | agent->merge_global(AddLatency(latency), _combiner); |
128 | 0 | return *this; |
129 | 0 | } |
130 | | #else |
131 | | Percentile::value_type Percentile::reset() { |
132 | | constexpr static size_t SAMPLE_SIZE = value_type::SAMPLE_SIZE; |
133 | | value_type result; |
134 | | _concurrent_sampler.for_each([&]( |
135 | | size_t index, const babylon::ConcurrentSampler::SampleBucket& bucket) { |
136 | | result.merge(bucket, index); |
137 | | auto capacity = _concurrent_sampler.bucket_capacity(index); |
138 | | auto num_added = bucket.record_num.load(::std::memory_order_relaxed); |
139 | | if (capacity < SAMPLE_SIZE && num_added > capacity) { |
140 | | capacity = std::min<size_t>(SAMPLE_SIZE, num_added * 1.5); |
141 | | _concurrent_sampler.set_bucket_capacity(index, capacity); |
142 | | } |
143 | | }); |
144 | | _concurrent_sampler.reset(); |
145 | | return result; |
146 | | } |
147 | | |
148 | | Percentile& Percentile::operator<<(int64_t value) { |
149 | | if (BAIDU_UNLIKELY(value < 0)) { |
150 | | // we don't check overflow(of uint32) in percentile because the |
151 | | // overflowed value which is included in last range does not affect |
152 | | // overall distribution of other values too much. |
153 | | if (!_debug_name.empty()) { |
154 | | LOG_EVERY_SECOND(WARNING) << "Input=" << value << " to `" << _debug_name |
155 | | << "' is negative, drop"; |
156 | | } else { |
157 | | LOG_EVERY_SECOND(WARNING) << "Input=" << value << " to Percentile(" |
158 | | << (void*)this << ") is negative, drop"; |
159 | | } |
160 | | } else { |
161 | | _concurrent_sampler << value; |
162 | | } |
163 | | return *this; |
164 | | } |
165 | | #endif // WITH_BABYLON_COUNTER |
166 | | } // namespace detail |
167 | | } // namespace bvar |