| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| // Date: 2015/09/15 15:14:32 |
| |
| #include "bvar/detail/percentile.h" |
| #include "butil/logging.h" |
| |
| namespace bvar { |
| namespace detail { |
| #if !WITH_BABYLON_COUNTER |
| inline uint32_t ones32(uint32_t x) { |
| /* 32-bit recursive reduction using SWAR... |
| * but first step is mapping 2-bit values |
| * into sum of 2 1-bit values in sneaky way |
| */ |
| x -= ((x >> 1) & 0x55555555); |
| x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); |
| x = (((x >> 4) + x) & 0x0f0f0f0f); |
| x += (x >> 8); |
| x += (x >> 16); |
| return (x & 0x0000003f); |
| } |
| |
| inline uint32_t log2(uint32_t x) { |
| int y = (x & (x - 1)); |
| y |= -y; |
| y >>= 31; |
| x |= (x >> 1); |
| x |= (x >> 2); |
| x |= (x >> 4); |
| x |= (x >> 8); |
| x |= (x >> 16); |
| return(ones32(x) - 1 - y); |
| } |
| |
| inline size_t get_interval_index(int64_t &x) { |
| if (x <= 2) { |
| return 0; |
| } else if (x > std::numeric_limits<uint32_t>::max()) { |
| x = std::numeric_limits<uint32_t>::max(); |
| return 31; |
| } else { |
| return log2(x) - 1; |
| } |
| } |
| |
| class AddLatency { |
| public: |
| AddLatency(int64_t latency) : _latency(latency) {} |
| |
| void operator()(GlobalValue<Percentile::combiner_type>& global_value, |
| ThreadLocalPercentileSamples& local_value) const { |
| // Copy to latency since get_interval_index may change input. |
| int64_t latency = _latency; |
| const size_t index = get_interval_index(latency); |
| PercentileInterval<ThreadLocalPercentileSamples::SAMPLE_SIZE>& |
| interval = local_value.get_interval_at(index); |
| if (interval.full()) { |
| GlobalPercentileSamples* g = global_value.lock(); |
| g->get_interval_at(index).merge(interval); |
| g->_num_added += interval.added_count(); |
| global_value.unlock(); |
| local_value._num_added -= interval.added_count(); |
| interval.clear(); |
| } |
| interval.add64(latency); |
| ++local_value._num_added; |
| } |
| private: |
| int64_t _latency; |
| }; |
| |
| Percentile::Percentile() |
| : _combiner(std::make_shared<combiner_type>()), _sampler(nullptr) {} |
| |
| Percentile::~Percentile() { |
| // Have to destroy sampler first to avoid the race between destruction and |
| // sampler |
| if (_sampler != nullptr) { |
| _sampler->destroy(); |
| _sampler = nullptr; |
| } |
| } |
| |
| Percentile::value_type Percentile::reset() { |
| return _combiner->reset_all_agents(); |
| } |
| |
| Percentile::value_type Percentile::get_value() const { |
| return _combiner->combine_agents(); |
| } |
| |
| Percentile &Percentile::operator<<(int64_t latency) { |
| agent_type* agent = _combiner->get_or_create_tls_agent(); |
| if (BAIDU_UNLIKELY(!agent)) { |
| LOG(FATAL) << "Fail to create agent"; |
| return *this; |
| } |
| if (latency < 0) { |
| // we don't check overflow(of uint32) in percentile because the |
| // overflowed value which is included in last range does not affect |
| // overall distribution of other values too much. |
| if (!_debug_name.empty()) { |
| LOG(WARNING) << "Input=" << latency << " to `" << _debug_name |
| << "' is negative, drop"; |
| } else { |
| LOG(WARNING) << "Input=" << latency << " to Percentile(" |
| << (void*)this << ") is negative, drop"; |
| } |
| return *this; |
| } |
| agent->merge_global(AddLatency(latency), _combiner); |
| return *this; |
| } |
| #else |
| Percentile::value_type Percentile::reset() { |
| constexpr static size_t SAMPLE_SIZE = value_type::SAMPLE_SIZE; |
| value_type result; |
| _concurrent_sampler.for_each([&]( |
| size_t index, const babylon::ConcurrentSampler::SampleBucket& bucket) { |
| result.merge(bucket, index); |
| auto capacity = _concurrent_sampler.bucket_capacity(index); |
| auto num_added = bucket.record_num.load(::std::memory_order_relaxed); |
| if (capacity < SAMPLE_SIZE && num_added > capacity) { |
| capacity = std::min<size_t>(SAMPLE_SIZE, num_added * 1.5); |
| _concurrent_sampler.set_bucket_capacity(index, capacity); |
| } |
| }); |
| _concurrent_sampler.reset(); |
| return result; |
| } |
| |
| Percentile& Percentile::operator<<(int64_t value) { |
| if (BAIDU_UNLIKELY(value < 0)) { |
| // we don't check overflow(of uint32) in percentile because the |
| // overflowed value which is included in last range does not affect |
| // overall distribution of other values too much. |
| if (!_debug_name.empty()) { |
| LOG_EVERY_SECOND(WARNING) << "Input=" << value << " to `" << _debug_name |
| << "' is negative, drop"; |
| } else { |
| LOG_EVERY_SECOND(WARNING) << "Input=" << value << " to Percentile(" |
| << (void*)this << ") is negative, drop"; |
| } |
| } else { |
| _concurrent_sampler << value; |
| } |
| return *this; |
| } |
| #endif // WITH_BABYLON_COUNTER |
| } // namespace detail |
| } // namespace bvar |