blob: d676f1a61a1d53354f44d0f613c9f11fc9e6d819 [file]
// 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