Coverage Report

Created: 2026-02-02 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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