Coverage Report

Created: 2025-12-17 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/brpc/src/bvar/detail/percentile.h
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 10:44:17
19
20
#ifndef  BVAR_DETAIL_PERCENTILE_H
21
#define  BVAR_DETAIL_PERCENTILE_H
22
23
#include <string.h>                     // memset memcmp
24
#include <stdint.h>                     // uint32_t
25
#include <limits>                       // std::numeric_limits
26
#include <ostream>                      // std::ostream
27
#include <algorithm>                    // std::sort
28
#include <math.h>                       // ceil
29
#include "butil/macros.h"                // ARRAY_SIZE
30
#include "bvar/reducer.h"               // Reducer
31
#include "bvar/window.h"                // Window
32
#include "bvar/detail/combiner.h"       // AgentCombiner
33
#include "bvar/detail/sampler.h"        // ReducerSampler
34
#include "butil/fast_rand.h"
35
#if WITH_BABYLON_COUNTER
36
#include "babylon/concurrent/counter.h"
37
#endif // WITH_BABYLON_COUNTER
38
39
namespace bvar {
40
namespace detail {
41
42
// Round of expectation of a rational number |a/b| to a natural number.
43
0
inline unsigned long round_of_expectation(unsigned long a, unsigned long b) {
44
0
    if (BAIDU_UNLIKELY(b == 0)) {
45
0
        return 0;
46
0
    }
47
0
    return a / b + (butil::fast_rand_less_than(b) < a % b);
48
0
}
49
50
// Storing latencies inside a interval.
51
template <size_t SAMPLE_SIZE>
52
class PercentileInterval {
53
public:
54
    PercentileInterval()
55
0
        : _num_added(0)
56
0
        , _sorted(false)
57
0
        , _num_samples(0) {
58
0
    }
Unexecuted instantiation: bvar::detail::PercentileInterval<254ul>::PercentileInterval()
Unexecuted instantiation: bvar::detail::PercentileInterval<1022ul>::PercentileInterval()
Unexecuted instantiation: bvar::detail::PercentileInterval<30ul>::PercentileInterval()
59
60
    // Get index-th sample in ascending order.
61
0
    uint32_t get_sample_at(size_t index) {
62
0
        const size_t saved_num = _num_samples;
63
0
        if (index >= saved_num) {
64
0
            if (saved_num == 0) {
65
0
                return 0;
66
0
            }
67
0
            index = saved_num - 1;
68
0
        }
69
0
        if (!_sorted) {
70
0
            std::sort(_samples, _samples + saved_num);
71
0
            _sorted = true;
72
0
        }
73
0
        CHECK_EQ(saved_num, _num_samples) << "You must call get_number() on"
74
0
            " a unchanging PercentileInterval";
75
0
        return _samples[index];
76
0
    }
77
78
    // Add samples of another interval. This function tries to make each
79
    // sample in merged _samples has (approximately) equal probability to
80
    // remain.
81
    // This method is invoked when merging ThreadLocalPercentileSamples in to
82
    // GlobalPercentileSamples
83
    template <size_t size2>
84
0
    void merge(const PercentileInterval<size2> &rhs) {
85
0
        if (rhs._num_added == 0) {
86
0
            return;
87
0
        }
88
0
        BAIDU_CASSERT(SAMPLE_SIZE >= size2,
89
0
                      must_merge_small_interval_into_larger_one_currently);
90
0
        CHECK_EQ(rhs._num_samples, rhs._num_added);
91
        // Assume that the probability of each sample in |this| is a0/b0 and
92
        // the probability of each sample in |rhs| is a1/b1.
93
        // We are going to randomly pick some samples from |this| and |rhs| to
94
        // satisfy the constraint that each sample stands for the probability
95
        // of 
96
        //     * 1 (SAMPLE_SIZE >= |b0 + b1|), which indicates that no sample
97
        //       has been dropped 
98
        //     * SAMPLE_SIZE / |b0 + b1| (SAMPLE_SIZE < |b0 + b1|)
99
        // So we should keep |b0*SAMPLE_SIZE/(b0+b1)| from |this|
100
        // |b1*SAMPLE_SIZE/(b0+b1)| from |rhs|.
101
0
        if (_num_added + rhs._num_added <= SAMPLE_SIZE) {
102
            // No sample should be dropped
103
0
            CHECK_EQ(_num_samples, _num_added)
104
0
                << "_num_added=" << _num_added
105
0
                << " rhs._num_added=" << rhs._num_added
106
0
                << " _num_samples=" << _num_samples
107
0
                << " rhs._num_samples=" << rhs._num_samples
108
0
                << " SAMPLE_SIZE=" << SAMPLE_SIZE
109
0
                << " size2=" << size2;
110
0
            memcpy(_samples + _num_samples, rhs._samples, 
111
0
                   sizeof(_samples[0]) * rhs._num_samples);
112
0
            _num_samples += rhs._num_samples;
113
0
        } else {
114
            // |num_remain| must be less than _num_samples:
115
            // if _num_added = _num_samples:
116
            //    SAMPLE_SIZE / (_num_added + rhs._num_added) < 1 so that
117
            //    num_remain < _num_added = _num_samples
118
            // otherwise:
119
            //    _num_samples = SAMPLE_SIZE;
120
            //    _num_added / (_num_added + rhs._num_added) < 1 so that
121
            //    num_remain < SAMPLE_SIZE = _num_added
122
0
            size_t num_remain = round_of_expectation(
123
0
                    _num_added * SAMPLE_SIZE, _num_added + rhs._num_added);
124
0
            CHECK_LE(num_remain, _num_samples);
125
            // Randomly drop samples of this
126
0
            for (size_t i = _num_samples; i > num_remain; --i) {
127
0
                _samples[butil::fast_rand_less_than(i)] = _samples[i - 1];
128
0
            }
129
0
            const size_t num_remain_from_rhs = SAMPLE_SIZE - num_remain;
130
0
            CHECK_LE(num_remain_from_rhs, rhs._num_samples);
131
            // Have to copy data from rhs to shuffle since it's const
132
0
            DEFINE_SMALL_ARRAY(uint32_t, tmp, rhs._num_samples, 64);
133
0
            memcpy(tmp, rhs._samples, sizeof(uint32_t) * rhs._num_samples);
134
0
            for (size_t i = 0; i < num_remain_from_rhs; ++i) {
135
0
                const int index = butil::fast_rand_less_than(rhs._num_samples - i);
136
0
                _samples[num_remain++] = tmp[index];
137
0
                tmp[index] = tmp[rhs._num_samples - i - 1];
138
0
            }
139
0
            _num_samples = num_remain;
140
0
            CHECK_EQ(_num_samples, SAMPLE_SIZE);
141
0
        }
142
0
        _num_added += rhs._num_added;
143
0
    }
Unexecuted instantiation: void bvar::detail::PercentileInterval<254ul>::merge<254ul>(bvar::detail::PercentileInterval<254ul> const&)
Unexecuted instantiation: void bvar::detail::PercentileInterval<254ul>::merge<30ul>(bvar::detail::PercentileInterval<30ul> const&)
144
145
#if WITH_BABYLON_COUNTER
146
    size_t merge(const babylon::ConcurrentSampler::SampleBucket& bucket) {
147
        auto num_added = bucket.record_num.load(std::memory_order_acquire);
148
        if (num_added == 0) {
149
            return 0;
150
        }
151
        auto num_samples = std::min(num_added, static_cast<uint32_t>(bucket.capacity));
152
        // If there is space, deposit directly.
153
        if (_num_samples + num_samples <= SAMPLE_SIZE) {
154
            __builtin_memcpy(_samples + _num_samples, bucket.data,
155
                             sizeof(uint32_t) * num_samples);
156
            _num_samples += num_samples;
157
        } else {
158
            // Sample probability weighting.
159
            float ratio = static_cast<float>(num_samples) / num_added;
160
            // Try to deposit directly first.
161
            if (_num_samples < SAMPLE_SIZE) {
162
                auto copy_size = SAMPLE_SIZE - _num_samples;
163
                num_samples -= copy_size;
164
                __builtin_memcpy(_samples + _num_samples,
165
                                 bucket.data + num_samples, sizeof(uint32_t) * copy_size);
166
            }
167
            // The remaining samples are stored according to probability.
168
            for (size_t i = 0; i < num_samples; ++i) {
169
                auto index = butil::fast_rand() %
170
                    static_cast<uint64_t>((_num_added + i) * ratio + 1);
171
                if (index < SAMPLE_SIZE) {
172
                    _samples[index] = bucket.data[i];
173
                }
174
            }
175
            _num_samples = SAMPLE_SIZE;
176
        }
177
        _num_added += num_added;
178
        return num_added;
179
    }
180
#endif // WITH_BABYLON_COUNTER
181
182
    // Randomly pick n samples from mutable_rhs to |this|
183
    template <size_t size2>
184
0
    void merge_with_expectation(PercentileInterval<size2>& mutable_rhs, size_t n) {
185
0
        CHECK(n <= mutable_rhs._num_samples);
186
0
        _num_added += mutable_rhs._num_added;
187
0
        if (_num_samples + n <= SAMPLE_SIZE && n == mutable_rhs._num_samples) {
188
0
            memcpy(_samples + _num_samples, mutable_rhs._samples, sizeof(_samples[0]) * n);
189
0
            _num_samples += n;
190
0
            return;
191
0
        }
192
0
        for (size_t i = 0; i < n; ++i) {
193
0
            size_t index = butil::fast_rand_less_than(mutable_rhs._num_samples - i);
194
0
            if (_num_samples < SAMPLE_SIZE) {
195
0
                _samples[_num_samples++] = mutable_rhs._samples[index];
196
0
            } else {
197
0
                _samples[butil::fast_rand_less_than(_num_samples)]
198
0
                        = mutable_rhs._samples[index];
199
0
            }
200
0
            std::swap(mutable_rhs._samples[index],
201
0
                      mutable_rhs._samples[mutable_rhs._num_samples - i - 1]);
202
0
        }
203
0
    }
204
205
    // Add an unsigned 32-bit latency (what percentile actually accepts) to a
206
    // non-full interval, which is invoked by Percentile::operator<< to add a
207
    // sample into the ThreadLocalPercentileSamples.
208
    // Returns true if the input was stored.
209
0
    bool add32(uint32_t x) {
210
0
        if (BAIDU_UNLIKELY(_num_samples >= SAMPLE_SIZE)) {
211
0
            LOG(ERROR) << "This interval was full";
212
0
            return false;
213
0
        }
214
0
        ++_num_added;
215
0
        _samples[_num_samples++] = x;
216
0
        return true;
217
0
    }
218
219
    // Add a signed latency.
220
0
    bool add64(int64_t x) {
221
0
        if (x >= 0) {
222
0
            return add32((uint32_t)x);
223
0
        }
224
0
        return false;
225
0
    }
226
227
    // Remove all samples inside.
228
0
    void clear() {
229
0
        _num_added = 0;
230
0
        _sorted = false;
231
0
        _num_samples = 0;
232
0
    }
Unexecuted instantiation: bvar::detail::PercentileInterval<254ul>::clear()
Unexecuted instantiation: bvar::detail::PercentileInterval<1022ul>::clear()
Unexecuted instantiation: bvar::detail::PercentileInterval<30ul>::clear()
233
234
    // True if no more room for new samples.
235
0
    bool full() const { return _num_samples == SAMPLE_SIZE; }
236
237
    // True if there's no samples.
238
0
    bool empty() const { return !_num_samples; }
Unexecuted instantiation: bvar::detail::PercentileInterval<254ul>::empty() const
Unexecuted instantiation: bvar::detail::PercentileInterval<30ul>::empty() const
239
240
    // #samples ever added by calling add*()
241
0
    uint32_t added_count() const { return _num_added; }
Unexecuted instantiation: bvar::detail::PercentileInterval<254ul>::added_count() const
Unexecuted instantiation: bvar::detail::PercentileInterval<1022ul>::added_count() const
Unexecuted instantiation: bvar::detail::PercentileInterval<30ul>::added_count() const
242
243
    // #samples stored.
244
0
    uint32_t sample_count() const { return _num_samples; }
Unexecuted instantiation: bvar::detail::PercentileInterval<254ul>::sample_count() const
Unexecuted instantiation: bvar::detail::PercentileInterval<1022ul>::sample_count() const
245
246
    // For debuggin.
247
0
    void describe(std::ostream &os) const {
248
0
        os << "(num_added=" << added_count() << ")[";
249
0
        for (size_t j = 0; j < _num_samples; ++j) {
250
0
            os << ' ' << _samples[j];
251
0
        }
252
0
        os << " ]";
253
0
    }
254
255
    // True if two PercentileInterval are exactly same, namely same # of added and
256
    // same samples, mainly for debuggin.
257
    bool operator==(const PercentileInterval& rhs) const {
258
        return (_num_added == rhs._num_added &&
259
                _num_samples == rhs._num_samples &&
260
                memcmp(_samples, rhs._samples,  _num_samples * sizeof(uint32_t)) == 0);
261
    }
262
263
private:
264
template <size_t size2> friend class PercentileInterval;
265
    BAIDU_CASSERT(SAMPLE_SIZE <= 65536, SAMPLE_SIZE_must_be_16bit);
266
267
    uint32_t _num_added;
268
    bool _sorted;
269
    uint16_t _num_samples;
270
    uint32_t _samples[SAMPLE_SIZE];
271
};
272
273
static const size_t NUM_INTERVALS = 32;
274
275
// This declartion is a must for gcc 3.4
276
class AddLatency;
277
278
// Group of PercentileIntervals.
279
template <size_t SAMPLE_SIZE_IN>
280
class PercentileSamples {
281
public:
282
#if !WITH_BABYLON_COUNTER
283
friend class AddLatency;
284
#endif // WITH_BABYLON_COUNTER
285
286
    static const size_t SAMPLE_SIZE = SAMPLE_SIZE_IN;
287
    
288
0
    PercentileSamples() {
289
0
        memset(this, 0, sizeof(*this));
290
0
    }
Unexecuted instantiation: bvar::detail::PercentileSamples<254ul>::PercentileSamples()
Unexecuted instantiation: bvar::detail::PercentileSamples<1022ul>::PercentileSamples()
Unexecuted instantiation: bvar::detail::PercentileSamples<30ul>::PercentileSamples()
291
292
0
    ~PercentileSamples() {
293
0
        for (size_t i = 0; i < NUM_INTERVALS; ++i) {
294
0
            if (_intervals[i]) {
295
0
                delete _intervals[i];
296
0
            }
297
0
        }
298
0
    }
Unexecuted instantiation: bvar::detail::PercentileSamples<254ul>::~PercentileSamples()
Unexecuted instantiation: bvar::detail::PercentileSamples<1022ul>::~PercentileSamples()
Unexecuted instantiation: bvar::detail::PercentileSamples<30ul>::~PercentileSamples()
299
300
    // Copy-construct from another PercentileSamples.
301
    // Copy/assigning happen at per-second scale. should be OK.
302
0
    PercentileSamples(const PercentileSamples& rhs) {
303
0
        _num_added = rhs._num_added;
304
0
        for (size_t i = 0; i < NUM_INTERVALS; ++i) {
305
0
            if (rhs._intervals[i] && !rhs._intervals[i]->empty()) {
306
0
                _intervals[i] = new PercentileInterval<SAMPLE_SIZE>(*rhs._intervals[i]);
307
0
            } else {
308
0
                _intervals[i] = NULL;
309
0
            }
310
0
        }
311
0
    }
Unexecuted instantiation: bvar::detail::PercentileSamples<254ul>::PercentileSamples(bvar::detail::PercentileSamples<254ul> const&)
Unexecuted instantiation: bvar::detail::PercentileSamples<30ul>::PercentileSamples(bvar::detail::PercentileSamples<30ul> const&)
312
313
    // Assign from another PercentileSamples.
314
    // Notice that we keep empty _intervals to avoid future allocations.
315
0
    void operator=(const PercentileSamples& rhs) {
316
0
        _num_added = rhs._num_added;
317
0
        for (size_t i = 0; i < NUM_INTERVALS; ++i) {
318
0
            if (rhs._intervals[i] && !rhs._intervals[i]->empty()) {
319
0
                get_interval_at(i) = *rhs._intervals[i];
320
0
            } else if (_intervals[i]) {
321
0
                _intervals[i]->clear();
322
0
            }
323
0
        }
324
0
    }
Unexecuted instantiation: bvar::detail::PercentileSamples<254ul>::operator=(bvar::detail::PercentileSamples<254ul> const&)
Unexecuted instantiation: bvar::detail::PercentileSamples<30ul>::operator=(bvar::detail::PercentileSamples<30ul> const&)
325
    
326
    // Get the `ratio'-ile value. E.g. 0.99 means 99%-ile value.
327
    // Since we store samples in different intervals internally. We first
328
    // address the interval by multiplying ratio with _num_added, then
329
    // find the sample inside the interval. We've tested an alternative
330
    // method that store all samples together w/o any intervals (or in another
331
    // word, only one interval), the method is much simpler but is not as
332
    // stable as current impl. CDF plotted by the method changes dramatically
333
    // from seconds to seconds. It seems that separating intervals probably
334
    // keep more long-tail values.
335
0
    uint32_t get_number(double ratio) {
336
0
        size_t n = (size_t)ceil(ratio * _num_added);
337
0
        if (n > _num_added) {
338
0
            n = _num_added;
339
0
        } else if (n == 0) {
340
0
            return 0;
341
0
        }
342
0
        for (size_t i = 0; i < NUM_INTERVALS; ++i) {
343
0
            if (_intervals[i] == NULL) {
344
0
                continue;
345
0
            }
346
0
            PercentileInterval<SAMPLE_SIZE>& invl = *_intervals[i];
347
0
            if (n <= invl.added_count()) {
348
0
                size_t sample_n = n * invl.sample_count() / invl.added_count();
349
0
                size_t sample_index = (sample_n ? sample_n - 1 : 0);
350
0
                return invl.get_sample_at(sample_index);
351
0
            }
352
0
            n -= invl.added_count();
353
0
        }
354
0
        CHECK(false) << "Can't reach here";
355
0
        return std::numeric_limits<uint32_t>::max();
356
0
    }
357
358
    // Add samples in another PercentileSamples.
359
    template <size_t size2>
360
0
    void merge(const PercentileSamples<size2> &rhs) {
361
0
        _num_added += rhs._num_added;
362
0
        for (size_t i = 0; i < NUM_INTERVALS; ++i) {
363
0
            if (rhs._intervals[i] && !rhs._intervals[i]->empty()) {
364
0
                get_interval_at(i).merge(*rhs._intervals[i]);
365
0
            }
366
0
        }
367
0
    }
Unexecuted instantiation: void bvar::detail::PercentileSamples<254ul>::merge<254ul>(bvar::detail::PercentileSamples<254ul> const&)
Unexecuted instantiation: void bvar::detail::PercentileSamples<254ul>::merge<30ul>(bvar::detail::PercentileSamples<30ul> const&)
368
369
#if WITH_BABYLON_COUNTER
370
    void merge(const babylon::ConcurrentSampler::SampleBucket& bucket, size_t index) {
371
        _num_added += get_interval_at(index).merge(bucket);
372
    }
373
#endif // WITH_BABYLON_COUNTER
374
375
    // Combine multiple into a single PercentileSamples
376
    template <typename Iterator>
377
0
    void combine_of(const Iterator& begin, const Iterator& end) {
378
0
        if (_num_added) {
379
            // Very unlikely
380
0
            for (size_t i = 0; i < NUM_INTERVALS; ++i) {
381
0
                if (_intervals[i]) {
382
0
                    _intervals[i]->clear();
383
0
                }
384
0
            }
385
0
            _num_added = 0;
386
0
        }
387
388
0
        for (Iterator iter = begin; iter != end; ++iter) {
389
0
            _num_added += iter->_num_added;
390
0
        }
391
392
        // Calculate probabilities for each interval
393
0
        for (size_t i = 0; i < NUM_INTERVALS; ++i) {
394
0
            size_t total = 0;
395
0
            size_t total_sample=0;
396
0
            for (Iterator iter = begin; iter != end; ++iter) {
397
0
                if (iter->_intervals[i]) {
398
0
                    total += iter->_intervals[i]->added_count();
399
0
                    total_sample += iter->_intervals[i]->sample_count();
400
0
                }
401
0
            }
402
0
            if (total == 0) {
403
                // Empty interval
404
0
                continue;
405
0
            }
406
407
408
            // Consider that sub interval took |a| samples out of |b| totally,
409
            // each sample won the probability of |a/b| according to the
410
            // algorithm of add32(), now we should pick some samples into the
411
            // combined interval that satisfied each sample has the
412
            // probability of |SAMPLE_SIZE/total|, so each sample has the
413
            // probability of |(SAMPLE_SIZE*b)/(a*total) to remain and the
414
            // expected number of samples in this interval is
415
            // |(SAMPLE_SIZE*b)/total|
416
0
            for (Iterator iter = begin; iter != end; ++iter) {
417
0
                if (!iter->_intervals[i] || iter->_intervals[i]->empty()) {
418
0
                    continue;
419
0
                }
420
0
                typename butil::add_reference<BAIDU_TYPEOF(*(iter->_intervals[i]))>::type
421
0
                        invl = *(iter->_intervals[i]);
422
0
                if (total <= SAMPLE_SIZE) {
423
0
                    get_interval_at(i).merge_with_expectation(
424
0
                            invl, invl.sample_count());
425
0
                    continue;
426
0
                }
427
                // Each 
428
0
                const size_t b = invl.added_count();
429
0
                const size_t remain = std::min(
430
0
                        round_of_expectation(b * SAMPLE_SIZE, total),
431
0
                        (size_t)invl.sample_count());
432
0
                get_interval_at(i).merge_with_expectation(invl, remain);
433
0
            }
434
0
        }
435
0
    }
436
437
   // For debuggin.
438
0
    void describe(std::ostream &os) const {
439
0
        os << this << "{num_added=" << _num_added;
440
0
        for (size_t i = 0; i < NUM_INTERVALS; ++i) {
441
0
            if (_intervals[i] && !_intervals[i]->empty()) {
442
0
                os << " interval[" << i << "]=";
443
0
                _intervals[i]->describe(os);
444
0
            }
445
0
        }
446
0
        os << '}';
447
0
    }
448
449
    // True if intervals of two PercentileSamples are exactly same.
450
    bool operator==(const PercentileSamples& rhs) const {
451
        for (size_t i = 0; i < NUM_INTERVALS; ++i) {
452
            if (_intervals != rhs._intervals[i]) {
453
                return false;
454
            }
455
        }
456
        return true;
457
    }
458
459
private:
460
template <size_t size1> friend class PercentileSamples;
461
462
    // Get/create interval on-demand.
463
0
    PercentileInterval<SAMPLE_SIZE>& get_interval_at(size_t index) {
464
0
        if (_intervals[index] == NULL) {
465
0
            _intervals[index] = new PercentileInterval<SAMPLE_SIZE>;
466
0
        }
467
0
        return *_intervals[index];
468
0
    }
Unexecuted instantiation: bvar::detail::PercentileSamples<254ul>::get_interval_at(unsigned long)
Unexecuted instantiation: bvar::detail::PercentileSamples<1022ul>::get_interval_at(unsigned long)
Unexecuted instantiation: bvar::detail::PercentileSamples<30ul>::get_interval_at(unsigned long)
469
    // sum of _num_added of all intervals. we update this value after any
470
    // changes to intervals inside to make it O(1)-time accessible.
471
    size_t _num_added;
472
    PercentileInterval<SAMPLE_SIZE>* _intervals[NUM_INTERVALS];
473
};
474
475
template <size_t sz> const size_t PercentileSamples<sz>::SAMPLE_SIZE;
476
477
template <size_t size>
478
std::ostream &operator<<(std::ostream &os, const PercentileInterval<size> &p) {
479
    p.describe(os);
480
    return os;
481
}
482
483
template <size_t size>
484
0
std::ostream &operator<<(std::ostream &os, const PercentileSamples<size> &p) {
485
0
    p.describe(os);
486
0
    return os;
487
0
}
488
489
// NOTE: we intentionally minus 2 uint32_t from sample-size to make the struct
490
// size be power of 2 and more friendly to memory allocators.
491
typedef PercentileSamples<254> GlobalPercentileSamples;
492
typedef PercentileSamples<30> ThreadLocalPercentileSamples;
493
494
namespace detail {
495
struct AddPercentileSamples {
496
    template <size_t size1, size_t size2>
497
    void operator()(PercentileSamples<size1> &b1,
498
0
                    const PercentileSamples<size2> &b2) const {
499
0
        b1.merge(b2);
500
0
    }
Unexecuted instantiation: void bvar::detail::detail::AddPercentileSamples::operator()<254ul, 254ul>(bvar::detail::PercentileSamples<254ul>&, bvar::detail::PercentileSamples<254ul> const&) const
Unexecuted instantiation: void bvar::detail::detail::AddPercentileSamples::operator()<254ul, 30ul>(bvar::detail::PercentileSamples<254ul>&, bvar::detail::PercentileSamples<30ul> const&) const
501
};
502
} // namespace detail
503
504
// A specialized reducer for finding the percentile of latencies.
505
// NOTE: DON'T use it directly, use LatencyRecorder instead.
506
#if !WITH_BABYLON_COUNTER
507
class Percentile {
508
public:
509
    typedef GlobalPercentileSamples value_type;
510
    typedef ReducerSampler<Percentile, GlobalPercentileSamples,
511
                           detail::AddPercentileSamples, VoidOp>sampler_type;
512
    typedef AgentCombiner <GlobalPercentileSamples,
513
                           ThreadLocalPercentileSamples,
514
                           detail::AddPercentileSamples> combiner_type;
515
    typedef combiner_type::self_shared_type shared_combiner_type;
516
    typedef combiner_type::Agent agent_type;
517
518
    Percentile();
519
    ~Percentile();
520
521
0
    detail::AddPercentileSamples op() const {
522
0
        return detail::AddPercentileSamples();
523
0
    }
524
0
    VoidOp inv_op() const { return VoidOp(); }
525
526
    // The sampler for windows over percentile.
527
0
    sampler_type* get_sampler() {
528
0
        if (NULL == _sampler) {
529
0
            _sampler = new sampler_type(this);
530
0
            _sampler->schedule();
531
0
        }
532
0
        return _sampler;
533
0
    }
534
    
535
    value_type reset();
536
    
537
    value_type get_value() const;
538
    
539
    Percentile& operator<<(int64_t latency);
540
541
0
    bool valid() const { return _combiner != NULL && _combiner->valid(); }
542
    
543
    // This name is useful for warning negative latencies in operator<<
544
0
    void set_debug_name(const butil::StringPiece& name) {
545
0
        _debug_name.assign(name.data(), name.size());
546
0
    }
547
548
private:
549
    DISALLOW_COPY_AND_ASSIGN(Percentile);
550
551
    shared_combiner_type _combiner;
552
    sampler_type* _sampler;
553
    std::string _debug_name;
554
};
555
#else
556
class Percentile {
557
public:
558
    typedef GlobalPercentileSamples value_type;
559
    typedef detail::AddPercentileSamples AddPercentileSamples;
560
    typedef AddPercentileSamples Op;
561
    typedef VoidOp InvOp;
562
    typedef ReducerSampler<Percentile, value_type, Op, InvOp> sampler_type;
563
564
    Percentile() = default;
565
    DISALLOW_COPY_AND_MOVE(Percentile);
566
    ~Percentile() noexcept {
567
        if (NULL != _sampler) {
568
            _sampler->destroy();
569
        }
570
    }
571
572
    Op op() const { return Op(); }
573
    InvOp inv_op() const { return InvOp(); }
574
575
    sampler_type* get_sampler() {
576
        if (NULL == _sampler) {
577
            _sampler = new sampler_type(this);
578
            _sampler->schedule();
579
        }
580
        return _sampler;
581
    }
582
583
    value_type reset();
584
585
    value_type get_value() const {
586
        LOG_EVERY_SECOND(ERROR) << "Percentile should never call this get_value()";
587
        return value_type();
588
    }
589
590
    Percentile& operator<<(int64_t value);
591
592
    bool valid() const { return true; }
593
594
    // This name is useful for warning negative latencies in operator<<
595
    void set_debug_name(const butil::StringPiece& name) {
596
        _debug_name.assign(name.data(), name.size());
597
    }
598
599
private:
600
    babylon::ConcurrentSampler _concurrent_sampler;
601
    sampler_type* _sampler{NULL};
602
    std::string _debug_name;
603
};
604
#endif // WITH_BABYLON_COUNTER
605
606
}  // namespace detail
607
}  // namespace bvar
608
609
#endif  //BVAR_DETAIL_PERCENTILE_H