// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/metrics/sample_vector.h" #include "base/logging.h" #include "base/metrics/bucket_ranges.h" namespace base { typedef HistogramBase::Count Count; typedef HistogramBase::Sample Sample; SampleVector::SampleVector(const BucketRanges* bucket_ranges) : SampleVector(0, bucket_ranges) {} SampleVector::SampleVector(uint64_t id, const BucketRanges* bucket_ranges) : HistogramSamples(id), local_counts_(bucket_ranges->bucket_count()), counts_(&local_counts_[0]), counts_size_(local_counts_.size()), bucket_ranges_(bucket_ranges) { CHECK_GE(bucket_ranges_->bucket_count(), 1u); } SampleVector::SampleVector(uint64_t id, HistogramBase::AtomicCount* counts, size_t counts_size, Metadata* meta, const BucketRanges* bucket_ranges) : HistogramSamples(id, meta), counts_(counts), counts_size_(bucket_ranges->bucket_count()), bucket_ranges_(bucket_ranges) { CHECK_LE(bucket_ranges_->bucket_count(), counts_size_); CHECK_GE(bucket_ranges_->bucket_count(), 1u); } SampleVector::~SampleVector() {} void SampleVector::Accumulate(Sample value, Count count) { size_t bucket_index = GetBucketIndex(value); subtle::NoBarrier_Store(&counts_[bucket_index], subtle::NoBarrier_Load(&counts_[bucket_index]) + count); IncreaseSum(static_cast(count) * value); IncreaseRedundantCount(count); } Count SampleVector::GetCount(Sample value) const { size_t bucket_index = GetBucketIndex(value); return subtle::NoBarrier_Load(&counts_[bucket_index]); } Count SampleVector::TotalCount() const { Count count = 0; for (size_t i = 0; i < counts_size_; i++) { count += subtle::NoBarrier_Load(&counts_[i]); } return count; } Count SampleVector::GetCountAtIndex(size_t bucket_index) const { DCHECK(bucket_index < counts_size_); return subtle::NoBarrier_Load(&counts_[bucket_index]); } scoped_ptr SampleVector::Iterator() const { return scoped_ptr( new SampleVectorIterator(counts_, counts_size_, bucket_ranges_)); } bool SampleVector::AddSubtractImpl(SampleCountIterator* iter, HistogramSamples::Operator op) { HistogramBase::Sample min; HistogramBase::Sample max; HistogramBase::Count count; // Go through the iterator and add the counts into correct bucket. size_t index = 0; while (index < counts_size_ && !iter->Done()) { iter->Get(&min, &max, &count); if (min == bucket_ranges_->range(index) && max == bucket_ranges_->range(index + 1)) { // Sample matches this bucket! HistogramBase::Count old_counts = subtle::NoBarrier_Load(&counts_[index]); subtle::NoBarrier_Store(&counts_[index], old_counts + ((op == HistogramSamples::ADD) ? count : -count)); iter->Next(); } else if (min > bucket_ranges_->range(index)) { // Sample is larger than current bucket range. Try next. index++; } else { // Sample is smaller than current bucket range. We scan buckets from // smallest to largest, so the sample value must be invalid. return false; } } return iter->Done(); } // Use simple binary search. This is very general, but there are better // approaches if we knew that the buckets were linearly distributed. size_t SampleVector::GetBucketIndex(Sample value) const { size_t bucket_count = bucket_ranges_->bucket_count(); CHECK_GE(bucket_count, 1u); CHECK_GE(value, bucket_ranges_->range(0)); CHECK_LT(value, bucket_ranges_->range(bucket_count)); size_t under = 0; size_t over = bucket_count; size_t mid; do { DCHECK_GE(over, under); mid = under + (over - under)/2; if (mid == under) break; if (bucket_ranges_->range(mid) <= value) under = mid; else over = mid; } while (true); DCHECK_LE(bucket_ranges_->range(mid), value); CHECK_GT(bucket_ranges_->range(mid + 1), value); return mid; } SampleVectorIterator::SampleVectorIterator( const std::vector* counts, const BucketRanges* bucket_ranges) : counts_(&(*counts)[0]), counts_size_(counts->size()), bucket_ranges_(bucket_ranges), index_(0) { CHECK_GE(bucket_ranges_->bucket_count(), counts_size_); SkipEmptyBuckets(); } SampleVectorIterator::SampleVectorIterator( const HistogramBase::AtomicCount* counts, size_t counts_size, const BucketRanges* bucket_ranges) : counts_(counts), counts_size_(counts_size), bucket_ranges_(bucket_ranges), index_(0) { CHECK_GE(bucket_ranges_->bucket_count(), counts_size_); SkipEmptyBuckets(); } SampleVectorIterator::~SampleVectorIterator() {} bool SampleVectorIterator::Done() const { return index_ >= counts_size_; } void SampleVectorIterator::Next() { DCHECK(!Done()); index_++; SkipEmptyBuckets(); } void SampleVectorIterator::Get(HistogramBase::Sample* min, HistogramBase::Sample* max, HistogramBase::Count* count) const { DCHECK(!Done()); if (min != NULL) *min = bucket_ranges_->range(index_); if (max != NULL) *max = bucket_ranges_->range(index_ + 1); if (count != NULL) *count = subtle::NoBarrier_Load(&counts_[index_]); } bool SampleVectorIterator::GetBucketIndex(size_t* index) const { DCHECK(!Done()); if (index != NULL) *index = index_; return true; } void SampleVectorIterator::SkipEmptyBuckets() { if (Done()) return; while (index_ < counts_size_) { if (subtle::NoBarrier_Load(&counts_[index_]) != 0) return; index_++; } } } // namespace base