Improve ReportPerformance::Histogram implementation and add drawing

Test: dumpsys media.log -r
Bug: 68148948
Change-Id: Idde42ba6b152f15c8c1834c8eea4d7ca24b4c934
gugelfrei
Eric Tan 6 years ago
parent bd9b5935fd
commit cfa7c4b96c

@ -22,6 +22,7 @@
#include <algorithm> #include <algorithm>
#include <climits> #include <climits>
#include <deque> #include <deque>
#include <iomanip>
#include <math.h> #include <math.h>
#include <numeric> #include <numeric>
#include <sstream> #include <sstream>
@ -51,23 +52,21 @@ namespace ReportPerformance {
void Histogram::add(double value) void Histogram::add(double value)
{ {
// TODO Handle domain and range error exceptions? if (mBinSize <= 0 || mBins.size() < 2) {
const int binIndex = lround((value - mLow) / mBinSize); return;
if (binIndex < 0) {
mLowCount++;
} else if (binIndex >= mNumBins) {
mHighCount++;
} else {
mBins[binIndex]++;
} }
// TODO Handle domain and range error exceptions?
const int unboundedIndex = lround((value - mLow) / mBinSize) + 1;
// std::clamp is introduced in C++17
//const int index = std::clamp(unboundedIndex, 0, (int)(mBins.size() - 1));
const int index = std::max(0, std::min((int)(mBins.size() - 1), unboundedIndex));
mBins[index]++;
mTotalCount++; mTotalCount++;
} }
void Histogram::clear() void Histogram::clear()
{ {
std::fill(mBins.begin(), mBins.end(), 0); std::fill(mBins.begin(), mBins.end(), 0);
mLowCount = 0;
mHighCount = 0;
mTotalCount = 0; mTotalCount = 0;
} }
@ -81,27 +80,75 @@ std::string Histogram::toString() const {
static constexpr char kDivider = '|'; static constexpr char kDivider = '|';
ss << kVersion << "," << mBinSize << "," << mNumBins << "," << mLow << ",{"; ss << kVersion << "," << mBinSize << "," << mNumBins << "," << mLow << ",{";
bool first = true; bool first = true;
if (mLowCount != 0) { for (size_t i = 0; i < mBins.size(); i++) {
ss << "-1" << kDivider << mLowCount;
first = false;
}
for (size_t i = 0; i < mNumBins; i++) {
if (mBins[i] != 0) { if (mBins[i] != 0) {
if (!first) { if (!first) {
ss << ","; ss << ",";
} }
ss << i << kDivider << mBins[i]; ss << static_cast<int>(i) - 1 << kDivider << mBins[i];
first = false; first = false;
} }
} }
if (mHighCount != 0) { ss << "}";
if (!first) {
ss << ","; return ss.str();
}
std::string Histogram::asciiArtString(size_t indent) const {
if (totalCount() == 0 || mBinSize <= 0 || mBins.size() < 2) {
return "";
}
static constexpr char kMarker = '-';
// One increment is considered one step of a bin's height.
static constexpr size_t kMarkersPerIncrement = 2;
static constexpr size_t kMaxIncrements = 64 + 1;
static constexpr size_t kMaxNumberWidth = 7;
static const std::string kMarkers(kMarkersPerIncrement * kMaxIncrements, kMarker);
static const std::string kSpaces(kMarkersPerIncrement * kMaxIncrements, ' ');
// get the last n characters of s, or the whole string if it is shorter
auto getTail = [](const size_t n, const std::string &s) {
return s.c_str() + s.size() - std::min(n, s.size());
};
// Since totalCount() > 0, mBins is not empty and maxCount > 0.
const unsigned maxCount = *std::max_element(mBins.begin(), mBins.end());
const size_t maxIncrements = log2(maxCount) + 1;
std::stringstream ss;
// Non-zero bins must exist at this point because totalCount() > 0.
size_t firstNonZeroBin = 0;
// If firstNonZeroBin reaches mBins.size() - 1, then it must be a nonzero bin.
for (; firstNonZeroBin < mBins.size() - 1 && mBins[firstNonZeroBin] == 0; firstNonZeroBin++) {}
const size_t firstBinToPrint = firstNonZeroBin == 0 ? 0 : firstNonZeroBin - 1;
size_t lastNonZeroBin = mBins.size() - 1;
// If lastNonZeroBin reaches 0, then it must be a nonzero bin.
for (; lastNonZeroBin > 0 && mBins[lastNonZeroBin] == 0; lastNonZeroBin--) {}
const size_t lastBinToPrint = lastNonZeroBin == mBins.size() - 1 ? lastNonZeroBin
: lastNonZeroBin + 1;
for (size_t bin = firstBinToPrint; bin <= lastBinToPrint; bin++) {
ss << std::setw(indent + kMaxNumberWidth);
if (bin == 0) {
ss << "<";
} else if (bin == mBins.size() - 1) {
ss << ">";
} else {
ss << mLow + (bin - 1) * mBinSize;
} }
ss << mNumBins << kDivider << mHighCount; ss << " |";
first = false; size_t increments = 0;
const uint64_t binCount = mBins[bin];
if (binCount > 0) {
increments = log2(binCount) + 1;
ss << getTail(increments * kMarkersPerIncrement, kMarkers);
}
ss << getTail((maxIncrements - increments + 1) * kMarkersPerIncrement, kSpaces)
<< binCount << "\n";
} }
ss << "}"; ss << "\n";
return ss.str(); return ss.str();
} }

@ -58,16 +58,16 @@ public:
/** /**
* \brief Creates a Histogram object. * \brief Creates a Histogram object.
* *
* \param binSize the width of each bin of the histogram. * \param binSize the width of each bin of the histogram, must be greater than 0.
* Units are whatever data the caller decides to store. * Units are whatever data the caller decides to store.
* \param numBins the number of bins desired in the histogram range. * \param numBins the number of bins desired in the histogram range, must be greater than 0.
* \param low the lower bound of the histogram bucket values. * \param low the lower bound of the histogram bucket values.
* Units are whatever data the caller decides to store. * Units are whatever data the caller decides to store.
* Note that the upper bound can be calculated by the following: * Note that the upper bound can be calculated by the following:
* upper = lower + binSize * numBins. * upper = lower + binSize * numBins.
*/ */
Histogram(double binSize, size_t numBins, double low = 0.) Histogram(double binSize, size_t numBins, double low = 0.)
: mBinSize(binSize), mNumBins(numBins), mLow(low), mBins(mNumBins) {} : mBinSize(binSize), mNumBins(numBins), mLow(low), mBins(mNumBins + 2) {}
Histogram(const Config &c) Histogram(const Config &c)
: Histogram(c.binSize, c.numBins, c.low) {} : Histogram(c.binSize, c.numBins, c.low) {}
@ -112,18 +112,24 @@ public:
*/ */
std::string toString() const; std::string toString() const;
// Draw log scale sideways histogram as ASCII art and store as a std::string.
// Empty string is returned if totalCount() == 0.
std::string asciiArtString(size_t indent = 0) const;
private: private:
// Histogram version number. // Histogram version number.
static constexpr int kVersion = 1; static constexpr int kVersion = 1;
const double mBinSize; // Size of each bucket const double mBinSize; // Size of each bucket
const size_t mNumBins; // Number of buckets in histogram range const size_t mNumBins; // Number of buckets in range (excludes low and high)
const double mLow; // Lower bound of values const double mLow; // Lower bound of values
std::vector<int> mBins; // Data structure to store the actual histogram
// Data structure to store the actual histogram. Counts of bin values less than mLow
// are stored in mBins[0]. Bin index i corresponds to mBins[i+1]. Counts of bin values
// >= high are stored in mBins[mNumBins + 1].
std::vector<uint64_t> mBins;
int mLowCount = 0; // Number of values less than mLow uint64_t mTotalCount = 0; // Total number of values recorded
int mHighCount = 0; // Number of values >= mLow + mBinSize * mNumBins
uint64_t mTotalCount = 0; // Total number of values recorded
}; };
// This is essentially the same as class PerformanceAnalysis, but PerformanceAnalysis // This is essentially the same as class PerformanceAnalysis, but PerformanceAnalysis

Loading…
Cancel
Save