Chromium Code Reviews| Index: base/metrics/histogram.cc |
| =================================================================== |
| --- base/metrics/histogram.cc (revision 152603) |
| +++ base/metrics/histogram.cc (working copy) |
| @@ -31,7 +31,137 @@ |
| typedef HistogramBase::Count Count; |
| typedef HistogramBase::Sample Sample; |
| +BucketHistogramSamples::BucketHistogramSamples( |
| + const BucketRanges* bucket_ranges) |
| + : counts_(bucket_ranges->size() - 1), |
| + bucket_ranges_(bucket_ranges) { |
| + CHECK_GE(bucket_ranges_->size(), 2u); |
|
Ilya Sherman
2012/08/23 07:50:54
nit: Does this need to be a CHECK, or would a DCHE
kaiwang
2012/08/24 04:17:58
This check has litter performance impact, I don't
Ilya Sherman
2012/08/29 08:48:16
That is an argument for only ever using CHECKs, an
Ilya Sherman
2012/08/29 23:46:00
Brett or John, could one of you chime in on this q
|
| +} |
| +BucketHistogramSamples::~BucketHistogramSamples() {} |
| + |
| +void BucketHistogramSamples::Accumulate(Sample value, Count count) { |
| + size_t i = BucketIndex(value, bucket_ranges_); |
|
Ilya Sherman
2012/08/23 07:50:54
nit: i -> bucket (prefer to use descriptive names)
kaiwang
2012/08/24 04:17:58
i -> bucket_index
|
| + counts_[i] += count; |
| + sum_ += count * value; |
|
Ilya Sherman
2012/08/23 07:50:54
Any need to check for overflow?
kaiwang
2012/08/24 04:17:58
sum is int64 and count/value are both int32.
Also,
Ilya Sherman
2012/08/29 08:48:16
Why isn't clamping to the max value for int64 the
|
| + redundant_count_ += count; |
| +} |
| + |
| +Count BucketHistogramSamples::GetCount(Sample value) const { |
| + size_t i = BucketIndex(value, bucket_ranges_); |
| + return counts_[i]; |
| +} |
| + |
| +Count BucketHistogramSamples::TotalCount() const { |
| + Count count = 0; |
| + for (size_t i = 0; i < counts_.size(); i++) { |
| + count += counts_[i]; |
| + } |
| + return count; |
| +} |
| + |
| +Count BucketHistogramSamples::GetCountFromBucketIndex( |
| + size_t bucket_index) const { |
| + return counts_[bucket_index]; |
| +} |
| + |
| +scoped_ptr<SampleCountIterator> BucketHistogramSamples::Iterator() const { |
| + return scoped_ptr<SampleCountIterator>( |
| + new BucketHistogramSamplesIterator(&counts_, bucket_ranges_)); |
| +} |
| + |
| +// Use simple binary search. This is very general, but there are better |
| +// approaches if we knew that the buckets were linearly distributed. |
| +// |
| // static |
| +size_t BucketHistogramSamples::BucketIndex(Sample value, |
| + const BucketRanges* bucket_ranges) { |
| + size_t bucket_count = bucket_ranges->size() - 1; |
| + 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 { |
| + mid = under + (over - under)/2; |
| + if (mid == under) |
| + break; |
| + if (bucket_ranges->range(mid) <= value) |
| + under = mid; |
| + else |
| + over = mid; |
| + } while (true); |
|
Ilya Sherman
2012/08/23 07:50:54
Can we use a standard binary search implementation
kaiwang
2012/08/24 04:17:58
This code is copied from below.
I think for refact
|
| + return mid; |
| +} |
| + |
| +bool BucketHistogramSamples::AddSubtractHelper(SampleCountIterator* iter, |
| + bool is_add) { |
| + HistogramBase::Sample min; |
| + HistogramBase::Sample max; |
| + HistogramBase::Count count; |
| + |
| + size_t index = 0; |
| + while (index < counts_.size() && !iter->Done()) { |
|
Ilya Sherman
2012/08/23 07:50:54
Please add a comment describing what this double-i
kaiwang
2012/08/24 04:17:58
Done.
|
| + iter->Get(&min, &max, &count); |
| + if (min == bucket_ranges_->range(index) && |
| + max == bucket_ranges_->range(index + 1)) { |
| + // Bucket match! |
| + counts_[index] += is_add ? count : - count; |
|
Ilya Sherman
2012/08/23 07:50:54
nit: The unary minus operator should not be follow
kaiwang
2012/08/24 04:17:58
Done.
|
| + iter->Next(); |
| + } else if (min > bucket_ranges_->range(index)) { |
| + index++; |
| + } else { |
| + return false; |
| + } |
| + } |
| + |
| + return iter->Done(); |
| +} |
| + |
| +BucketHistogramSamplesIterator::BucketHistogramSamplesIterator( |
| + const vector<Count>* counts, |
| + const BucketRanges* bucket_ranges) |
| + : counts_(counts), |
| + bucket_ranges_(bucket_ranges), |
| + index_(-1), |
| + is_done_(false) { |
| + CHECK_GT(bucket_ranges_->size(), counts_->size()); |
| + ForwardIndex(); |
| +} |
| + |
| +bool BucketHistogramSamplesIterator::Done() { |
| + return is_done_; |
| +} |
| + |
| +void BucketHistogramSamplesIterator::Next() { |
| + ForwardIndex(); |
| +} |
| + |
| +void BucketHistogramSamplesIterator::Get(HistogramBase::Sample* min, |
| + HistogramBase::Sample* max, |
| + HistogramBase::Count* count) { |
| + CHECK(!Done()); |
| + if (min != NULL) |
|
Ilya Sherman
2012/08/23 07:50:54
nit: I saw nothing in the method description that
kaiwang
2012/08/24 04:17:58
added to interface comment
|
| + *min = bucket_ranges_->range(index_); |
| + if (max != NULL) |
| + *max = bucket_ranges_->range(index_ + 1); |
| + if (count != NULL) |
| + *count = (*counts_)[index_]; |
| +} |
| + |
| +void BucketHistogramSamplesIterator::ForwardIndex() { |
| + CHECK(!Done()); |
| + |
| + while (index_ < static_cast<int>(counts_->size()) - 1) { |
| + index_++; |
| + if ((*counts_)[index_] != 0) |
| + return; |
| + } |
| + is_done_ = true; |
| +} |
| + |
| +// static |
| const size_t Histogram::kBucketCount_MAX = 16384u; |
| Histogram::SampleSet::SampleSet(size_t size) |
| @@ -245,26 +375,26 @@ |
| ranges->ResetChecksum(); |
| } |
| -// static |
| void Histogram::Add(int value) { |
| - if (value > kSampleType_MAX - 1) |
| - value = kSampleType_MAX - 1; |
| - if (value < 0) |
| - value = 0; |
| - size_t index = BucketIndex(value); |
| - DCHECK_GE(value, ranges(index)); |
| - DCHECK_LT(value, ranges(index + 1)); |
| - Accumulate(value, 1, index); |
| + if (value > ranges(bucket_count_) - 1) |
|
jar (doing other things)
2012/08/30 22:54:38
You appear to have changed a constant comparisons
kaiwang
2012/09/07 01:14:11
Done.
|
| + value = ranges(bucket_count_) - 1; |
| + if (value < ranges(0)) |
| + value = ranges(0); |
| + samples_->Accumulate(value, 1); |
| } |
| void Histogram::AddBoolean(bool value) { |
| DCHECK(false); |
| } |
| -void Histogram::AddSampleSet(const SampleSet& sample) { |
| - sample_.Add(sample); |
| +void Histogram::AddSamples(const HistogramSamples& samples) { |
| + samples_->Add(samples); |
| } |
| +bool Histogram::AddSamples(PickleIterator* iter) { |
| + return samples_->Add(iter); |
| +} |
| + |
| void Histogram::SetRangeDescriptions(const DescriptionPair descriptions[]) { |
| DCHECK(false); |
| } |
| @@ -283,7 +413,7 @@ |
| // static |
| string Histogram::SerializeHistogramInfo(const Histogram& histogram, |
| - const SampleSet& snapshot) { |
| + const HistogramSamples& snapshot) { |
| DCHECK_NE(NOT_VALID_IN_RENDERER, histogram.histogram_type()); |
| DCHECK(histogram.bucket_ranges()->HasValidChecksum()); |
| @@ -296,10 +426,10 @@ |
| pickle.WriteInt(histogram.histogram_type()); |
| pickle.WriteInt(histogram.flags()); |
| + histogram.SerializeRanges(&pickle); |
|
Ilya Sherman
2012/08/23 07:50:54
Why did you move this line?
kaiwang
2012/08/24 04:17:58
this is part of "histogram info", while the code b
|
| + |
| snapshot.Serialize(&pickle); |
| - histogram.SerializeRanges(&pickle); |
| - |
| return string(static_cast<const char*>(pickle.data()), pickle.size()); |
| } |
| @@ -318,7 +448,6 @@ |
| uint32 range_checksum; |
| int histogram_type; |
| int pickle_flags; |
| - SampleSet sample; |
| PickleIterator iter(pickle); |
| if (!iter.ReadString(&histogram_name) || |
| @@ -327,8 +456,7 @@ |
| !iter.ReadUInt64(&bucket_count) || |
| !iter.ReadUInt32(&range_checksum) || |
| !iter.ReadInt(&histogram_type) || |
| - !iter.ReadInt(&pickle_flags) || |
| - !sample.Histogram::SampleSet::Deserialize(&iter)) { |
|
jar (doing other things)
2012/08/30 22:54:38
How are these samples recovered? I don't see the
kaiwang
2012/09/07 01:14:11
See line 387, it adds all samples from a PickleIte
|
| + !iter.ReadInt(&pickle_flags)) { |
| DLOG(ERROR) << "Pickle error decoding Histogram: " << histogram_name; |
| return false; |
| } |
| @@ -382,23 +510,22 @@ |
| if (render_histogram->flags() & kIPCSerializationSourceFlag) { |
| DVLOG(1) << "Single process mode, histogram observed and not copied: " |
| << histogram_name; |
| - } else { |
| - DCHECK_EQ(flags & render_histogram->flags(), flags); |
| - render_histogram->AddSampleSet(sample); |
| + return true; |
| } |
| - return true; |
| + DCHECK_EQ(flags & render_histogram->flags(), flags); |
| + |
|
Ilya Sherman
2012/08/23 07:50:54
nit: No need for this blank line.
kaiwang
2012/08/24 04:17:58
Done.
|
| + return render_histogram->AddSamples(&iter); |
| } |
| +// static |
| +const int Histogram::kCommonRaceBasedCountMismatch = 5; |
|
Ilya Sherman
2012/08/23 07:50:54
Why did you move this constant into the Histogram
kaiwang
2012/08/24 04:17:58
It's needed in histogram_snapshot_manager.cc
The o
|
| -// Validate a sample and related histogram. |
|
Ilya Sherman
2012/08/23 07:50:54
nit: Why did you remove this comment?
kaiwang
2012/08/24 04:17:58
better function comment in .h file
|
| Histogram::Inconsistencies Histogram::FindCorruption( |
| - const SampleSet& snapshot) const { |
| + const HistogramSamples& samples) const { |
| int inconsistencies = NO_INCONSISTENCIES; |
| Sample previous_range = -1; // Bottom range is always 0. |
| - int64 count = 0; |
| for (size_t index = 0; index < bucket_count(); ++index) { |
| - count += snapshot.counts(index); |
| int new_range = ranges(index); |
| if (previous_range >= new_range) |
| inconsistencies |= BUCKET_ORDER_ERROR; |
| @@ -408,20 +535,11 @@ |
| if (!bucket_ranges()->HasValidChecksum()) |
| inconsistencies |= RANGE_CHECKSUM_ERROR; |
| - int64 delta64 = snapshot.redundant_count() - count; |
| + int64 delta64 = samples.redundant_count() - samples.TotalCount(); |
| if (delta64 != 0) { |
| int delta = static_cast<int>(delta64); |
| if (delta != delta64) |
| delta = INT_MAX; // Flag all giant errors as INT_MAX. |
| - // Since snapshots of histograms are taken asynchronously relative to |
| - // sampling (and snapped from different threads), it is pretty likely that |
| - // we'll catch a redundant count that doesn't match the sample count. We |
| - // allow for a certain amount of slop before flagging this as an |
| - // inconsistency. Even with an inconsistency, we'll snapshot it again (for |
| - // UMA in about a half hour, so we'll eventually get the data, if it was |
| - // not the result of a corruption. If histograms show that 1 is "too tight" |
| - // then we may try to use 2 or 3 for this slop value. |
| - const int kCommonRaceBasedCountMismatch = 5; |
| if (delta > 0) { |
| UMA_HISTOGRAM_COUNTS("Histogram.InconsistentCountHigh", delta); |
| if (delta > kCommonRaceBasedCountMismatch) |
| @@ -448,11 +566,11 @@ |
| return bucket_count_; |
| } |
| -// Do a safe atomic snapshot of sample data. |
| -// This implementation assumes we are on a safe single thread. |
| -void Histogram::SnapshotSample(SampleSet* sample) const { |
| - // Note locking not done in this version!!! |
| - *sample = sample_; |
| +scoped_ptr<BucketHistogramSamples> Histogram::SnapshotSamples() const { |
| + scoped_ptr<BucketHistogramSamples> samples( |
| + new BucketHistogramSamples(bucket_ranges())); |
| + samples->Add(*samples_); |
| + return samples.Pass(); |
| } |
| bool Histogram::HasConstructionArguments(Sample minimum, |
| @@ -471,8 +589,11 @@ |
| bucket_ranges_(ranges), |
| declared_min_(minimum), |
| declared_max_(maximum), |
| - bucket_count_(bucket_count), |
| - sample_(bucket_count) {} |
| + bucket_count_(bucket_count) { |
| + if (ranges) { |
| + samples_.reset(new BucketHistogramSamples(ranges)); |
| + } |
|
Ilya Sherman
2012/08/23 07:50:54
nit: No need for curly braces
kaiwang
2012/08/24 04:17:58
Done.
|
| +} |
| Histogram::~Histogram() { |
| if (StatisticsRecorder::dump_on_exit()) { |
| @@ -519,31 +640,6 @@ |
| return true; |
| } |
| -size_t Histogram::BucketIndex(Sample value) const { |
| - // Use simple binary search. This is very general, but there are better |
| - // approaches if we knew that the buckets were linearly distributed. |
| - DCHECK_LE(ranges(0), value); |
| - DCHECK_GT(ranges(bucket_count()), value); |
| - 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 (ranges(mid) <= value) |
| - under = mid; |
| - else |
| - over = mid; |
| - } while (true); |
| - |
| - DCHECK_LE(ranges(mid), value); |
| - CHECK_GT(ranges(mid+1), value); |
| - return mid; |
| -} |
| - |
| // Use the actual bucket widths (like a linear histogram) until the widths get |
| // over some transition value, and then use that transition width. Exponentials |
| // get so big so fast (and we don't expect to see a lot of entries in the large |
| @@ -567,12 +663,6 @@ |
| return result; |
| } |
| -// Update histogram data with new sample. |
| -void Histogram::Accumulate(Sample value, Count count, size_t index) { |
| - // Note locking not done in this version!!! |
|
Ilya Sherman
2012/08/23 07:50:54
nit: Please keep this comment around by the releva
kaiwang
2012/08/24 04:17:58
this function is not needed any more. All Histogra
|
| - sample_.Accumulate(value, count, index); |
| -} |
| - |
| //------------------------------------------------------------------------------ |
| // Private methods |
| @@ -581,22 +671,21 @@ |
| string* output) const { |
| // Get local (stack) copies of all effectively volatile class data so that we |
| // are consistent across our output activities. |
| - SampleSet snapshot; |
| - SnapshotSample(&snapshot); |
| - Count sample_count = snapshot.TotalCount(); |
| + scoped_ptr<BucketHistogramSamples> snapshot = SnapshotSamples(); |
| + Count sample_count = snapshot->TotalCount(); |
| - WriteAsciiHeader(snapshot, sample_count, output); |
| + WriteAsciiHeader(*snapshot, sample_count, output); |
| output->append(newline); |
| // Prepare to normalize graphical rendering of bucket contents. |
| double max_size = 0; |
| if (graph_it) |
| - max_size = GetPeakBucketSize(snapshot); |
| + max_size = GetPeakBucketSize(*snapshot); |
| // Calculate space needed to print bucket range numbers. Leave room to print |
| // nearly the largest bucket range without sliding over the histogram. |
| size_t largest_non_empty_bucket = bucket_count() - 1; |
| - while (0 == snapshot.counts(largest_non_empty_bucket)) { |
| + while (0 == snapshot->GetCountFromBucketIndex(largest_non_empty_bucket)) { |
| if (0 == largest_non_empty_bucket) |
| break; // All buckets are empty. |
| --largest_non_empty_bucket; |
| @@ -605,7 +694,7 @@ |
| // Calculate largest print width needed for any of our bucket range displays. |
| size_t print_width = 1; |
| for (size_t i = 0; i < bucket_count(); ++i) { |
| - if (snapshot.counts(i)) { |
| + if (snapshot->GetCountFromBucketIndex(i)) { |
| size_t width = GetAsciiBucketRange(i).size() + 1; |
| if (width > print_width) |
| print_width = width; |
| @@ -616,7 +705,7 @@ |
| int64 past = 0; |
| // Output the actual histogram graph. |
| for (size_t i = 0; i < bucket_count(); ++i) { |
| - Count current = snapshot.counts(i); |
| + Count current = snapshot->GetCountFromBucketIndex(i); |
| if (!current && !PrintEmptyBucket(i)) |
| continue; |
| remaining -= current; |
| @@ -624,9 +713,12 @@ |
| output->append(range); |
| for (size_t j = 0; range.size() + j < print_width + 1; ++j) |
| output->push_back(' '); |
| - if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) { |
| - while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) |
| + if (0 == current && i < bucket_count() - 1 && |
| + 0 == snapshot->GetCountFromBucketIndex(i + 1)) { |
| + while (i < bucket_count() - 1 && |
| + 0 == snapshot->GetCountFromBucketIndex(i + 1)) { |
| ++i; |
| + } |
| output->append("... "); |
| output->append(newline); |
| continue; // No reason to plot emptiness. |
| @@ -641,17 +733,18 @@ |
| DCHECK_EQ(sample_count, past); |
| } |
| -double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { |
| +double Histogram::GetPeakBucketSize( |
| + const BucketHistogramSamples& samples) const { |
| double max = 0; |
| for (size_t i = 0; i < bucket_count() ; ++i) { |
| - double current_size = GetBucketSize(snapshot.counts(i), i); |
| + double current_size = GetBucketSize(samples.GetCountFromBucketIndex(i), i); |
| if (current_size > max) |
| max = current_size; |
| } |
| return max; |
| } |
| -void Histogram::WriteAsciiHeader(const SampleSet& snapshot, |
| +void Histogram::WriteAsciiHeader(const BucketHistogramSamples& samples, |
| Count sample_count, |
| string* output) const { |
| StringAppendF(output, |
| @@ -659,9 +752,9 @@ |
| histogram_name().c_str(), |
| sample_count); |
| if (0 == sample_count) { |
| - DCHECK_EQ(snapshot.sum(), 0); |
| + DCHECK_EQ(samples.sum(), 0); |
| } else { |
| - double average = static_cast<float>(snapshot.sum()) / sample_count; |
| + double average = static_cast<float>(samples.sum()) / sample_count; |
| StringAppendF(output, ", average = %.1f", average); |
| } |