1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/metrics/sample_vector.h"
6
7 #include "base/logging.h"
8 #include "base/metrics/bucket_ranges.h"
9
10 namespace base {
11
12 typedef HistogramBase::Count Count;
13 typedef HistogramBase::Sample Sample;
14
SampleVector(const BucketRanges * bucket_ranges)15 SampleVector::SampleVector(const BucketRanges* bucket_ranges)
16 : SampleVector(0, bucket_ranges) {}
17
SampleVector(uint64_t id,const BucketRanges * bucket_ranges)18 SampleVector::SampleVector(uint64_t id, const BucketRanges* bucket_ranges)
19 : HistogramSamples(id),
20 local_counts_(bucket_ranges->bucket_count()),
21 counts_(&local_counts_[0]),
22 counts_size_(local_counts_.size()),
23 bucket_ranges_(bucket_ranges) {
24 CHECK_GE(bucket_ranges_->bucket_count(), 1u);
25 }
26
SampleVector(uint64_t id,HistogramBase::AtomicCount * counts,size_t,Metadata * meta,const BucketRanges * bucket_ranges)27 SampleVector::SampleVector(uint64_t id,
28 HistogramBase::AtomicCount* counts,
29 size_t /*counts_size*/,
30 Metadata* meta,
31 const BucketRanges* bucket_ranges)
32 : HistogramSamples(id, meta),
33 counts_(counts),
34 counts_size_(bucket_ranges->bucket_count()),
35 bucket_ranges_(bucket_ranges) {
36 CHECK_LE(bucket_ranges_->bucket_count(), counts_size_);
37 CHECK_GE(bucket_ranges_->bucket_count(), 1u);
38 }
39
~SampleVector()40 SampleVector::~SampleVector() {}
41
Accumulate(Sample value,Count count)42 void SampleVector::Accumulate(Sample value, Count count) {
43 size_t bucket_index = GetBucketIndex(value);
44 subtle::NoBarrier_AtomicIncrement(&counts_[bucket_index], count);
45 IncreaseSum(static_cast<int64_t>(count) * value);
46 IncreaseRedundantCount(count);
47 }
48
GetCount(Sample value) const49 Count SampleVector::GetCount(Sample value) const {
50 size_t bucket_index = GetBucketIndex(value);
51 return subtle::NoBarrier_Load(&counts_[bucket_index]);
52 }
53
TotalCount() const54 Count SampleVector::TotalCount() const {
55 Count count = 0;
56 for (size_t i = 0; i < counts_size_; i++) {
57 count += subtle::NoBarrier_Load(&counts_[i]);
58 }
59 return count;
60 }
61
GetCountAtIndex(size_t bucket_index) const62 Count SampleVector::GetCountAtIndex(size_t bucket_index) const {
63 DCHECK(bucket_index < counts_size_);
64 return subtle::NoBarrier_Load(&counts_[bucket_index]);
65 }
66
Iterator() const67 std::unique_ptr<SampleCountIterator> SampleVector::Iterator() const {
68 return std::unique_ptr<SampleCountIterator>(
69 new SampleVectorIterator(counts_, counts_size_, bucket_ranges_));
70 }
71
AddSubtractImpl(SampleCountIterator * iter,HistogramSamples::Operator op)72 bool SampleVector::AddSubtractImpl(SampleCountIterator* iter,
73 HistogramSamples::Operator op) {
74 HistogramBase::Sample min;
75 HistogramBase::Sample max;
76 HistogramBase::Count count;
77
78 // Go through the iterator and add the counts into correct bucket.
79 size_t index = 0;
80 while (index < counts_size_ && !iter->Done()) {
81 iter->Get(&min, &max, &count);
82 if (min == bucket_ranges_->range(index) &&
83 max == bucket_ranges_->range(index + 1)) {
84 // Sample matches this bucket!
85 subtle::NoBarrier_AtomicIncrement(
86 &counts_[index], op == HistogramSamples::ADD ? count : -count);
87 iter->Next();
88 } else if (min > bucket_ranges_->range(index)) {
89 // Sample is larger than current bucket range. Try next.
90 index++;
91 } else {
92 // Sample is smaller than current bucket range. We scan buckets from
93 // smallest to largest, so the sample value must be invalid.
94 return false;
95 }
96 }
97
98 return iter->Done();
99 }
100
101 // Use simple binary search. This is very general, but there are better
102 // approaches if we knew that the buckets were linearly distributed.
GetBucketIndex(Sample value) const103 size_t SampleVector::GetBucketIndex(Sample value) const {
104 size_t bucket_count = bucket_ranges_->bucket_count();
105 CHECK_GE(bucket_count, 1u);
106 CHECK_GE(value, bucket_ranges_->range(0));
107 CHECK_LT(value, bucket_ranges_->range(bucket_count));
108
109 size_t under = 0;
110 size_t over = bucket_count;
111 size_t mid;
112 do {
113 DCHECK_GE(over, under);
114 mid = under + (over - under)/2;
115 if (mid == under)
116 break;
117 if (bucket_ranges_->range(mid) <= value)
118 under = mid;
119 else
120 over = mid;
121 } while (true);
122
123 DCHECK_LE(bucket_ranges_->range(mid), value);
124 CHECK_GT(bucket_ranges_->range(mid + 1), value);
125 return mid;
126 }
127
SampleVectorIterator(const std::vector<HistogramBase::AtomicCount> * counts,const BucketRanges * bucket_ranges)128 SampleVectorIterator::SampleVectorIterator(
129 const std::vector<HistogramBase::AtomicCount>* counts,
130 const BucketRanges* bucket_ranges)
131 : counts_(&(*counts)[0]),
132 counts_size_(counts->size()),
133 bucket_ranges_(bucket_ranges),
134 index_(0) {
135 CHECK_GE(bucket_ranges_->bucket_count(), counts_size_);
136 SkipEmptyBuckets();
137 }
138
SampleVectorIterator(const HistogramBase::AtomicCount * counts,size_t counts_size,const BucketRanges * bucket_ranges)139 SampleVectorIterator::SampleVectorIterator(
140 const HistogramBase::AtomicCount* counts,
141 size_t counts_size,
142 const BucketRanges* bucket_ranges)
143 : counts_(counts),
144 counts_size_(counts_size),
145 bucket_ranges_(bucket_ranges),
146 index_(0) {
147 CHECK_GE(bucket_ranges_->bucket_count(), counts_size_);
148 SkipEmptyBuckets();
149 }
150
~SampleVectorIterator()151 SampleVectorIterator::~SampleVectorIterator() {}
152
Done() const153 bool SampleVectorIterator::Done() const {
154 return index_ >= counts_size_;
155 }
156
Next()157 void SampleVectorIterator::Next() {
158 DCHECK(!Done());
159 index_++;
160 SkipEmptyBuckets();
161 }
162
Get(HistogramBase::Sample * min,HistogramBase::Sample * max,HistogramBase::Count * count) const163 void SampleVectorIterator::Get(HistogramBase::Sample* min,
164 HistogramBase::Sample* max,
165 HistogramBase::Count* count) const {
166 DCHECK(!Done());
167 if (min != NULL)
168 *min = bucket_ranges_->range(index_);
169 if (max != NULL)
170 *max = bucket_ranges_->range(index_ + 1);
171 if (count != NULL)
172 *count = subtle::NoBarrier_Load(&counts_[index_]);
173 }
174
GetBucketIndex(size_t * index) const175 bool SampleVectorIterator::GetBucketIndex(size_t* index) const {
176 DCHECK(!Done());
177 if (index != NULL)
178 *index = index_;
179 return true;
180 }
181
SkipEmptyBuckets()182 void SampleVectorIterator::SkipEmptyBuckets() {
183 if (Done())
184 return;
185
186 while (index_ < counts_size_) {
187 if (subtle::NoBarrier_Load(&counts_[index_]) != 0)
188 return;
189 index_++;
190 }
191 }
192
193 } // namespace base
194