Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(163)

Side by Side Diff: base/metrics/histogram_samples.cc

Issue 2811713003: Embed a single sample in histogram metadata. (Closed)
Patch Set: move single-sample from 'value' to 'bucket' Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/metrics/histogram_samples.h" 5 #include "base/metrics/histogram_samples.h"
6 6
7 #include <limits>
8
7 #include "base/compiler_specific.h" 9 #include "base/compiler_specific.h"
10 #include "base/numerics/safe_math.h"
8 #include "base/pickle.h" 11 #include "base/pickle.h"
9 12
10 namespace base { 13 namespace base {
11 14
12 namespace { 15 namespace {
13 16
17 // A shorthand constant for the max value of size_t.
18 constexpr size_t kSizeMax = std::numeric_limits<size_t>::max();
19
20 // A constant stored in an AtomicSingleSample (as_atomic) to indicate that the
21 // sample is "disabled" and no further accumulation should be done with it. The
22 // value is chosen such that it will be MAX_UINT16 for both |bucket| & |count|,
23 // and thus less likely to conflict with real use. Conflicts are explicitly
24 // handled in the code but it's worth making them as unlikely as possible.
25 constexpr int32_t kDisabledSingleSample = -1;
26
14 class SampleCountPickleIterator : public SampleCountIterator { 27 class SampleCountPickleIterator : public SampleCountIterator {
15 public: 28 public:
16 explicit SampleCountPickleIterator(PickleIterator* iter); 29 explicit SampleCountPickleIterator(PickleIterator* iter);
17 30
18 bool Done() const override; 31 bool Done() const override;
19 void Next() override; 32 void Next() override;
20 void Get(HistogramBase::Sample* min, 33 void Get(HistogramBase::Sample* min,
21 HistogramBase::Sample* max, 34 HistogramBase::Sample* max,
22 HistogramBase::Count* count) const override; 35 HistogramBase::Count* count) const override;
23 36
(...skipping 28 matching lines...) Expand all
52 HistogramBase::Sample* max, 65 HistogramBase::Sample* max,
53 HistogramBase::Count* count) const { 66 HistogramBase::Count* count) const {
54 DCHECK(!Done()); 67 DCHECK(!Done());
55 *min = min_; 68 *min = min_;
56 *max = max_; 69 *max = max_;
57 *count = count_; 70 *count = count_;
58 } 71 }
59 72
60 } // namespace 73 } // namespace
61 74
75 static_assert(sizeof(HistogramSamples::AtomicSingleSample) ==
76 sizeof(subtle::Atomic32),
77 "AtomicSingleSample isn't 32 bits");
78
79 HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Load()
80 const {
81 AtomicSingleSample single_sample = subtle::Acquire_Load(&as_atomic);
82
83 // If the sample was extracted/disabled, it's still zero to the outside.
84 if (single_sample.as_atomic == kDisabledSingleSample)
85 single_sample.as_atomic = 0;
86
87 return single_sample.as_parts;
88 }
89
90 HistogramSamples::SingleSample HistogramSamples::AtomicSingleSample::Extract(
91 bool disable) {
92 AtomicSingleSample single_sample = subtle::NoBarrier_AtomicExchange(
93 &as_atomic, disable ? kDisabledSingleSample : 0);
94 if (single_sample.as_atomic == kDisabledSingleSample)
95 single_sample.as_atomic = 0;
96 return single_sample.as_parts;
97 }
98
99 bool HistogramSamples::AtomicSingleSample::Accumulate(
100 size_t bucket,
101 HistogramBase::Count count) {
102 if (count == 0)
103 return true;
104
105 // Convert the parameters to 16-bit variables because it's all 16-bit below.
106 if (count < std::numeric_limits<uint16_t>::min() ||
107 count > std::numeric_limits<uint16_t>::max() ||
108 bucket > std::numeric_limits<uint16_t>::max()) {
109 return false;
110 }
111 uint16_t bucket16 = static_cast<uint16_t>(bucket);
112 uint16_t count16 = static_cast<uint16_t>(count);
113
114 // A local, unshared copy of the single-sample is necessary so the parts
115 // can be manipulated without worrying about atomicity.
116 AtomicSingleSample single_sample;
117
118 subtle::Atomic32 existing;
119 subtle::Atomic32 found;
Alexei Svitkine (slow) 2017/04/20 19:56:10 Nit: How about renaming found to updated_value - s
bcwhite 2017/04/20 21:18:07 I think "updated_value" could be construed as the
Alexei Svitkine (slow) 2017/04/20 22:11:34 True, updated_value isn't a great name. How about
bcwhite 2017/04/21 13:25:31 Done. I've also changed the names from existing/f
120 do {
121 existing = subtle::Acquire_Load(&as_atomic);
122 if (existing == kDisabledSingleSample)
123 return false;
124 single_sample.as_atomic = existing;
125 if (single_sample.as_atomic != 0) {
126 // Only the same bucket (parameter and stored) can be counted multiple
127 // times.
128 if (single_sample.as_parts.bucket != bucket16)
129 return false;
130 } else {
131 // The single sample was zero so becomes the |bucket| parameter, the
Alexei Svitkine (slow) 2017/04/20 19:56:10 Nit: "single sample" -> "|single_sample|" to make
bcwhite 2017/04/20 21:18:07 Done.
132 // contents of which where checked above to fit in 16 bits.
Alexei Svitkine (slow) 2017/04/20 19:56:10 Nit: where -> were
bcwhite 2017/04/20 21:18:07 Done.
133 single_sample.as_parts.bucket = bucket16;
134 }
135
136 // Update count, making sure that it doesn't overflow.
137 CheckedNumeric<uint16_t> new_count(single_sample.as_parts.count);
138 new_count += count16;
139 if (!new_count.AssignIfValid(&single_sample.as_parts.count))
140 return false;
141
142 // Store the updated single-sample back into memory.
143 if (single_sample.as_atomic == kDisabledSingleSample)
144 return false;
145 found = subtle::Release_CompareAndSwap(&as_atomic, existing,
146 single_sample.as_atomic);
147 } while (found != existing);
148
149 return true;
150 }
151
152 bool HistogramSamples::AtomicSingleSample::IsDisabled() const {
153 return subtle::Acquire_Load(&as_atomic) == kDisabledSingleSample;
154 }
155
156 HistogramSamples::LocalMetadata::LocalMetadata() {
157 // This is the same way it's done for persistent metadata since no ctor
158 // is called for the data members in that case.
159 memset(this, 0, sizeof(*this));
160 }
161
62 // Don't try to delegate behavior to the constructor below that accepts a 162 // Don't try to delegate behavior to the constructor below that accepts a
63 // Matadata pointer by passing &local_meta_. Such cannot be reliably passed 163 // Matadata pointer by passing &local_meta_. Such cannot be reliably passed
64 // because it has not yet been constructed -- no member variables have; the 164 // because it has not yet been constructed -- no member variables have; the
65 // class itself is in the middle of being constructed. Using it to 165 // class itself is in the middle of being constructed. Using it to
66 // initialize meta_ is okay because the object now exists and local_meta_ 166 // initialize meta_ is okay because the object now exists and local_meta_
67 // is before meta_ in the construction order. 167 // is before meta_ in the construction order.
68 HistogramSamples::HistogramSamples(uint64_t id) 168 HistogramSamples::HistogramSamples(uint64_t id)
69 : meta_(&local_meta_) { 169 : meta_(&local_meta_) {
70 meta_->id = id; 170 meta_->id = id;
71 } 171 }
72 172
73 HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta) 173 HistogramSamples::HistogramSamples(uint64_t id, Metadata* meta)
74 : meta_(meta) { 174 : meta_(meta) {
75 DCHECK(meta_->id == 0 || meta_->id == id); 175 DCHECK(meta_->id == 0 || meta_->id == id);
76 176
77 // It's possible that |meta| is contained in initialized, read-only memory 177 // It's possible that |meta| is contained in initialized, read-only memory
78 // so it's essential that no write be done in that case. 178 // so it's essential that no write be done in that case.
79 if (!meta_->id) 179 if (!meta_->id)
80 meta_->id = id; 180 meta_->id = id;
81 } 181 }
82 182
83 HistogramSamples::~HistogramSamples() {} 183 HistogramSamples::~HistogramSamples() {}
84 184
85 void HistogramSamples::Add(const HistogramSamples& other) { 185 void HistogramSamples::Add(const HistogramSamples& other) {
86 IncreaseSum(other.sum()); 186 IncreaseSumAndCount(other.sum(), other.redundant_count());
87 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count,
88 other.redundant_count());
89 bool success = AddSubtractImpl(other.Iterator().get(), ADD); 187 bool success = AddSubtractImpl(other.Iterator().get(), ADD);
90 DCHECK(success); 188 DCHECK(success);
91 } 189 }
92 190
93 bool HistogramSamples::AddFromPickle(PickleIterator* iter) { 191 bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
94 int64_t sum; 192 int64_t sum;
95 HistogramBase::Count redundant_count; 193 HistogramBase::Count redundant_count;
96 194
97 if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count)) 195 if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count))
98 return false; 196 return false;
99 197
100 IncreaseSum(sum); 198 IncreaseSumAndCount(sum, redundant_count);
101 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count,
102 redundant_count);
103 199
104 SampleCountPickleIterator pickle_iter(iter); 200 SampleCountPickleIterator pickle_iter(iter);
105 return AddSubtractImpl(&pickle_iter, ADD); 201 return AddSubtractImpl(&pickle_iter, ADD);
106 } 202 }
107 203
108 void HistogramSamples::Subtract(const HistogramSamples& other) { 204 void HistogramSamples::Subtract(const HistogramSamples& other) {
109 IncreaseSum(-other.sum()); 205 IncreaseSumAndCount(-other.sum(), -other.redundant_count());
110 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count,
111 -other.redundant_count());
112 bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT); 206 bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT);
113 DCHECK(success); 207 DCHECK(success);
114 } 208 }
115 209
116 bool HistogramSamples::Serialize(Pickle* pickle) const { 210 bool HistogramSamples::Serialize(Pickle* pickle) const {
117 if (!pickle->WriteInt64(sum())) 211 if (!pickle->WriteInt64(sum()))
118 return false; 212 return false;
119 if (!pickle->WriteInt(redundant_count())) 213 if (!pickle->WriteInt(redundant_count()))
120 return false; 214 return false;
121 215
122 HistogramBase::Sample min; 216 HistogramBase::Sample min;
123 HistogramBase::Sample max; 217 HistogramBase::Sample max;
124 HistogramBase::Count count; 218 HistogramBase::Count count;
125 for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done(); 219 for (std::unique_ptr<SampleCountIterator> it = Iterator(); !it->Done();
126 it->Next()) { 220 it->Next()) {
127 it->Get(&min, &max, &count); 221 it->Get(&min, &max, &count);
128 if (!pickle->WriteInt(min) || 222 if (!pickle->WriteInt(min) ||
129 !pickle->WriteInt(max) || 223 !pickle->WriteInt(max) ||
130 !pickle->WriteInt(count)) 224 !pickle->WriteInt(count))
131 return false; 225 return false;
132 } 226 }
133 return true; 227 return true;
134 } 228 }
135 229
136 void HistogramSamples::IncreaseSum(int64_t diff) { 230 bool HistogramSamples::AccumulateSingleSample(HistogramBase::Sample value,
137 #ifdef ARCH_CPU_64_BITS 231 HistogramBase::Count count,
138 subtle::NoBarrier_AtomicIncrement(&meta_->sum, diff); 232 size_t bucket) {
139 #else 233 if (single_sample().Accumulate(bucket, count)) {
140 meta_->sum += diff; 234 // Success. Update the (separate) sum and redundant-count.
141 #endif 235 IncreaseSumAndCount(static_cast<int64_t>(value) * count, count);
236 return true;
237 }
238 return false;
142 } 239 }
143 240
144 void HistogramSamples::IncreaseRedundantCount(HistogramBase::Count diff) { 241 void HistogramSamples::IncreaseSumAndCount(int64_t sum,
145 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, diff); 242 HistogramBase::Count count) {
243 #ifdef ARCH_CPU_64_BITS
244 subtle::NoBarrier_AtomicIncrement(&meta_->sum, sum);
245 #else
246 meta_->sum += sum;
247 #endif
248 subtle::NoBarrier_AtomicIncrement(&meta_->redundant_count, count);
146 } 249 }
147 250
148 SampleCountIterator::~SampleCountIterator() {} 251 SampleCountIterator::~SampleCountIterator() {}
149 252
150 bool SampleCountIterator::GetBucketIndex(size_t* index) const { 253 bool SampleCountIterator::GetBucketIndex(size_t* index) const {
151 DCHECK(!Done()); 254 DCHECK(!Done());
152 return false; 255 return false;
153 } 256 }
154 257
258 SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
259 HistogramBase::Sample max,
260 HistogramBase::Count count)
261 : SingleSampleIterator(min, max, count, kSizeMax) {}
262
263 SingleSampleIterator::SingleSampleIterator(HistogramBase::Sample min,
264 HistogramBase::Sample max,
265 HistogramBase::Count count,
266 size_t bucket_index)
267 : min_(min), max_(max), bucket_index_(bucket_index), count_(count) {}
268
269 SingleSampleIterator::~SingleSampleIterator() {}
270
271 bool SingleSampleIterator::Done() const {
272 return count_ == 0;
273 }
274
275 void SingleSampleIterator::Next() {
276 DCHECK(!Done());
277 count_ = 0;
278 }
279
280 void SingleSampleIterator::Get(HistogramBase::Sample* min,
281 HistogramBase::Sample* max,
282 HistogramBase::Count* count) const {
283 DCHECK(!Done());
284 if (min != nullptr)
285 *min = min_;
286 if (max != nullptr)
287 *max = max_;
288 if (count != nullptr)
289 *count = count_;
290 }
291
292 bool SingleSampleIterator::GetBucketIndex(size_t* index) const {
293 DCHECK(!Done());
294 if (bucket_index_ == kSizeMax)
295 return false;
296 *index = bucket_index_;
297 return true;
298 }
299
155 } // namespace base 300 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698