Index: tracing/tracing/value/histogram.html |
diff --git a/tracing/tracing/value/histogram.html b/tracing/tracing/value/histogram.html |
index 32c1a467228788b3085c5cc8f51a88ae38da1708..3c5a8e6d2dbe117b31603a3f776edd5ca36398eb 100644 |
--- a/tracing/tracing/value/histogram.html |
+++ b/tracing/tracing/value/histogram.html |
@@ -55,20 +55,40 @@ tr.exportTo('tr.v', function() { |
this.count += other.count; |
} |
- fromDict(d) { |
- this.count = d.count; |
- for (var map of d.diagnosticMaps) |
- this.diagnosticMaps.push(tr.v.d.DiagnosticMap.fromDict(map)); |
+ fromDict(dict) { |
+ this.count = dict[0]; |
+ if (dict.length > 1) { |
+ for (var map of dict[1]) { |
+ this.diagnosticMaps.push(tr.v.d.DiagnosticMap.fromDict(map)); |
+ } |
+ } |
} |
asDict() { |
- return { |
- count: this.count, |
- diagnosticMaps: this.diagnosticMaps.map(d => d.asDict()) |
- }; |
+ if (!this.diagnosticMaps.length) { |
+ return [this.count]; |
+ } |
+ // It's more efficient to serialize these 2 fields in an array. If you |
+ // add any other fields, you should re-evaluate whether it would be more |
+ // efficient to serialize as a dict. |
+ return [this.count, this.diagnosticMaps.map(d => d.asDict())]; |
} |
} |
+ var DEFAULT_SUMMARY_OPTIONS = new Map([ |
+ ['avg', true], |
+ ['geometricMean', false], |
+ ['std', true], |
+ ['count', true], |
+ ['sum', true], |
+ ['min', true], |
+ ['max', true], |
+ ['nans', false], |
+ // Don't include 'percentile' here. Its default value is [], which is |
+ // modifiable. Callers may push to it, so there must be a different Array |
+ // instance for each Histogram instance. |
+ ]); |
+ |
/** |
* This is basically a histogram, but so much more. |
* Histogram is serializable using asDict/fromDict. |
@@ -98,7 +118,10 @@ tr.exportTo('tr.v', function() { |
// allocated the first time the guid is gotten by asDict(). |
this.guid_ = undefined; |
- this.allBins = []; |
+ // Serialize binBoundaries here instead of holding a reference to it in |
+ // case it is modified. |
+ this.binBoundariesDict_ = binBoundaries.asDict(); |
+ |
this.centralBins = []; |
this.description = ''; |
this.diagnostics = new tr.v.d.DiagnosticMap(); |
@@ -109,33 +132,35 @@ tr.exportTo('tr.v', function() { |
this.running = new tr.b.RunningStatistics(); |
this.sampleValues_ = []; |
this.shortName = undefined; |
- this.summaryOptions = { |
- avg: true, |
- geometricMean: false, |
- std: true, |
- count: true, |
- sum: true, |
- min: true, |
- max: true, |
- nans: false, |
- percentile: [] |
- }; |
+ this.summaryOptions = new Map(DEFAULT_SUMMARY_OPTIONS); |
+ this.summaryOptions.set('percentile', []); |
this.unit = unit; |
this.underflowBin = new HistogramBin(tr.b.Range.fromExplicitRange( |
- -Number.MAX_VALUE, binBoundaries.minBinBoundary)); |
+ -Number.MAX_VALUE, binBoundaries.range.min)); |
this.overflowBin = new HistogramBin(tr.b.Range.fromExplicitRange( |
- binBoundaries.maxBinBoundary, Number.MAX_VALUE)); |
+ binBoundaries.range.max, Number.MAX_VALUE)); |
- for (var range of binBoundaries) |
+ for (var range of binBoundaries.binRanges()) { |
this.centralBins.push(new HistogramBin(range)); |
+ } |
- this.allBins.push(this.underflowBin); |
+ this.allBins = [this.underflowBin]; |
for (var bin of this.centralBins) |
this.allBins.push(bin); |
this.allBins.push(this.overflowBin); |
- this.maxNumSampleValues = this.allBins.length * 10; |
+ this.maxNumSampleValues_ = this.defaultMaxNumSampleValues_; |
+ } |
+ |
+ get maxNumSampleValues() { |
+ return this.maxNumSampleValues_; |
+ } |
+ |
+ set maxNumSampleValues(n) { |
+ this.maxNumSampleValues_ = n; |
+ tr.b.Statistics.uniformlySampleArray( |
+ this.sampleValues_, this.maxNumSampleValues_); |
} |
get name() { |
@@ -156,36 +181,62 @@ tr.exportTo('tr.v', function() { |
this.guid_ = guid; |
} |
- static fromDict(d) { |
- var boundaries = HistogramBinBoundaries.createWithBoundaries( |
- d.binBoundaries); |
- var n = new Histogram(d.name, tr.b.Unit.fromJSON(d.unit), boundaries); |
- n.guid = d.guid; |
- n.shortName = d.shortName; |
- n.description = d.description; |
- n.diagnostics.addDicts(d.diagnostics); |
- |
- n.underflowBin.fromDict(d.underflowBin); |
- for (var i = 0; i < d.centralBins.length; ++i) |
- n.centralBins[i].fromDict(d.centralBins[i]); |
- n.overflowBin.fromDict(d.overflowBin); |
- |
- for (var bin of n.allBins) |
- n.maxCount_ = Math.max(n.maxCount_, bin.count); |
- |
- if (d.running) |
- n.running = tr.b.RunningStatistics.fromDict(d.running); |
- if (d.summaryOptions) |
- n.customizeSummaryOptions(d.summaryOptions); |
- |
- n.maxNumSampleValues = d.maxNumSampleValues; |
- n.sampleValues_ = d.sampleValues; |
- |
- n.numNans = d.numNans; |
- for (var map of d.nanDiagnosticMaps) |
- n.nanDiagnosticMaps.push(tr.v.d.DiagnosticMap.fromDict(map)); |
- |
- return n; |
+ static fromDict(dict) { |
+ var hist = new Histogram(dict.name, |
+ tr.b.Unit.fromJSON(dict.unit), |
+ HistogramBinBoundaries.fromDict( |
+ dict.binBoundaries)); |
+ hist.guid = dict.guid; |
+ if (dict.shortName) { |
+ hist.shortName = dict.shortName; |
+ } |
+ if (dict.description) { |
+ hist.description = dict.description; |
+ } |
+ if (dict.diagnostics) { |
+ hist.diagnostics.addDicts(dict.diagnostics); |
+ } |
+ if (dict.underflowBin) { |
+ hist.underflowBin.fromDict(dict.underflowBin); |
+ } |
+ if (dict.overflowBin) { |
+ hist.overflowBin.fromDict(dict.overflowBin); |
+ } |
+ if (dict.centralBins) { |
+ if (dict.centralBins.length !== undefined) { |
+ for (var i = 0; i < dict.centralBins.length; ++i) { |
+ hist.centralBins[i].fromDict(dict.centralBins[i]); |
+ } |
+ } else { |
+ tr.b.iterItems(dict.centralBins, (i, binDict) => { |
+ hist.centralBins[i].fromDict(binDict); |
+ }); |
+ } |
+ } |
+ for (var bin of hist.allBins) { |
+ hist.maxCount_ = Math.max(hist.maxCount_, bin.count); |
+ } |
+ if (dict.running) { |
+ hist.running = tr.b.RunningStatistics.fromDict(dict.running); |
+ } |
+ if (dict.summaryOptions) { |
+ hist.customizeSummaryOptions(dict.summaryOptions); |
+ } |
+ if (dict.maxNumSampleValues !== undefined) { |
+ hist.maxNumSampleValues = dict.maxNumSampleValues; |
+ } |
+ if (dict.sampleValues) { |
+ hist.sampleValues_ = dict.sampleValues; |
+ } |
+ if (dict.numNans) { |
+ hist.numNans = dict.numNans; |
+ } |
+ if (dict.nanDiagnostics) { |
+ for (var map of dict.nanDiagnostics) { |
+ hist.nanDiagnosticMaps.push(tr.v.d.DiagnosticMap.fromDict(map)); |
+ } |
+ } |
+ return hist; |
} |
/** |
@@ -390,9 +441,8 @@ tr.exportTo('tr.v', function() { |
* The options not included in the |summaryOptions| will not change. |
*/ |
customizeSummaryOptions(summaryOptions) { |
- tr.b.iterItems(summaryOptions, function(key, value) { |
- this.summaryOptions[key] = value; |
- }, this); |
+ tr.b.iterItems(summaryOptions, (key, value) => |
+ this.summaryOptions.set(key, value)); |
} |
/** |
@@ -441,16 +491,17 @@ tr.exportTo('tr.v', function() { |
} |
var results = new Map(); |
- tr.b.iterItems(this.summaryOptions, function(stat, option) { |
- if (!option) |
- return; |
+ for (var [stat, option] of this.summaryOptions) { |
+ if (!option) { |
+ continue; |
+ } |
if (stat === 'percentile') { |
- option.forEach(function(percent) { |
+ for (var percent of option) { |
var percentile = this.getApproximatePercentile(percent); |
results.set('pct_' + percentToString(percent), |
new tr.v.ScalarNumeric(this.unit, percentile)); |
- }, this); |
+ } |
} else if (stat === 'nans') { |
results.set('nans', new tr.v.ScalarNumeric( |
tr.b.Unit.byName.count_smallerIsBetter, this.numNans)); |
@@ -464,7 +515,7 @@ tr.exportTo('tr.v', function() { |
results.set(stat, new tr.v.ScalarNumeric(statUnit, statValue)); |
} |
} |
- }, this); |
+ } |
return results; |
} |
@@ -472,51 +523,143 @@ tr.exportTo('tr.v', function() { |
return this.sampleValues_; |
} |
- get binBoundaries() { |
- var boundaries = []; |
- for (var bin of this.centralBins) |
- boundaries.push(bin.range.min); |
- boundaries.push(this.overflowBin.range.min); |
- return boundaries; |
- } |
- |
+ /** |
+ * Create a new Histogram object that is exactly the same as this one, with |
+ * this Histogram's name, unit, and binBoundaries, guid, bin counts, and |
+ * diagnostics. |
+ * @return {!tr.v.Histogram} |
+ */ |
clone() { |
return Histogram.fromDict(this.asDict()); |
} |
+ /** |
+ * Create a new Histogram with this Histogram's name, unit, and |
+ * binBoundaries, but not its guid, bin counts, or diagnostics. |
+ * @return {!tr.v.Histogram} |
+ */ |
+ cloneEmpty() { |
+ var binBoundaries = HistogramBinBoundaries.fromDict( |
+ this.binBoundariesDict_); |
+ return new Histogram(this.name, this.unit, binBoundaries); |
+ } |
+ |
asDict() { |
- return { |
- name: this.name, |
- guid: this.guid, |
- shortName: this.shortName, |
- description: this.description, |
- diagnostics: this.diagnostics.asDict(), |
- unit: this.unit.asJSON(), |
- binBoundaries: this.binBoundaries, |
+ var dict = {}; |
+ dict.binBoundaries = this.binBoundariesDict_; |
+ dict.name = this.name; |
+ dict.unit = this.unit.asJSON(); |
+ dict.guid = this.guid; |
+ if (this.shortName) { |
+ dict.shortName = this.shortName; |
+ } |
+ if (this.description) { |
+ dict.description = this.description; |
+ } |
+ if (this.diagnostics.size) { |
+ dict.diagnostics = this.diagnostics.asDict(); |
+ } |
+ if (this.maxNumSampleValues !== this.defaultMaxNumSampleValues_) { |
+ dict.maxNumSampleValues = this.maxNumSampleValues; |
+ } |
+ if (this.numNans) { |
+ dict.numNans = this.numNans; |
+ } |
+ if (this.nanDiagnosticMaps.length) { |
+ dict.nanDiagnostics = this.nanDiagnosticMaps.map( |
+ dm => dm.asDict()); |
+ } |
+ if (this.underflowBin.count) { |
+ dict.underflowBin = this.underflowBin.asDict(); |
+ } |
+ if (this.overflowBin.count) { |
+ dict.overflowBin = this.overflowBin.asDict(); |
+ } |
- underflowBin: this.underflowBin.asDict(), |
- centralBins: this.centralBins.map(bin => bin.asDict()), |
- overflowBin: this.overflowBin.asDict(), |
+ if (this.numValues) { |
+ dict.sampleValues = this.sampleValues.slice(); |
+ dict.running = this.running.asDict(); |
+ dict.centralBins = this.centralBinsAsDict_(); |
+ } |
- running: this.running.asDict(), |
- summaryOptions: this.summaryOptions, |
+ var summaryOptions = {}; |
+ var anyOverriddenSummaryOptions = false; |
+ for (var [name, option] of this.summaryOptions) { |
+ if (name === 'percentile') { |
+ if (option.length === 0) { |
+ continue; |
+ } |
+ option = option.slice(); |
+ } else if (option === DEFAULT_SUMMARY_OPTIONS.get(name)) { |
+ continue; |
+ } |
+ summaryOptions[name] = option; |
+ anyOverriddenSummaryOptions = true; |
+ } |
+ if (anyOverriddenSummaryOptions) { |
+ dict.summaryOptions = summaryOptions; |
+ } |
+ |
+ return dict; |
+ } |
- maxNumSampleValues: this.maxNumSampleValues, |
- sampleValues: this.sampleValues, |
+ centralBinsAsDict_() { |
+ // dict.centralBins may be either an array or a dict, whichever is more |
+ // efficient. |
+ // The overhead of the array form is significant when the histogram is |
+ // sparse, and the overhead of the dict form is significant when the |
+ // histogram is dense. |
+ // The dict form is more efficient when more than half of centralBins are |
+ // empty. The array form is more efficient when fewer than half of |
+ // centralBins are empty. |
- numNans: this.numNans, |
- nanDiagnosticMaps: this.nanDiagnosticMaps.map(dm => dm.asDict()), |
- }; |
+ var numCentralBins = this.centralBins.length; |
+ |
+ // If all centralBins are empty, then don't serialize anything for them. |
+ var emptyBins = 0; |
+ |
+ for (var i = 0; i < numCentralBins; ++i) { |
+ if (this.centralBins[i].count === 0) { |
+ ++emptyBins; |
+ } |
+ } |
+ |
+ if (emptyBins === numCentralBins) { |
+ return undefined; |
+ } |
+ |
+ if (emptyBins > (numCentralBins / 2)) { |
+ var centralBinsDict = {}; |
+ for (var i = 0; i < numCentralBins; ++i) { |
+ var bin = this.centralBins[i]; |
+ if (bin.count > 0) { |
+ centralBinsDict[i] = bin.asDict(); |
+ } |
+ } |
+ return centralBinsDict; |
+ } |
+ |
+ var centralBinsArray = []; |
+ for (var i = 0; i < numCentralBins; ++i) { |
+ centralBinsArray.push(this.centralBins[i].asDict()); |
+ } |
+ return centralBinsArray; |
+ } |
+ |
+ get defaultMaxNumSampleValues_() { |
+ return this.allBins.length * 10; |
} |
} |
+ var HISTOGRAM_BIN_BOUNDARIES_CACHE = new Map(); |
+ |
/** |
* Reusable builder for tr.v.Histogram objects. |
* |
* The bins of the numeric are specified by adding the desired boundaries |
* between bins. Initially, the builder has only a single boundary: |
* |
- * minBinBoundary=maxBinBoundary |
+ * range.min=range.max |
* | |
* | |
* -MAX_INT <--------|------------------------------------------> +MAX_INT |
@@ -527,7 +670,7 @@ tr.exportTo('tr.v', function() { |
* More boundaries can be added (in increasing order) using addBinBoundary, |
* addLinearBins and addExponentialBins: |
* |
- * minBinBoundary maxBinBoundary |
+ * range.min range.max |
* | | | | | |
* | | | | | |
* -MAX_INT <--------|---------|---------|-----|---------|------> +MAX_INT |
@@ -538,11 +681,6 @@ tr.exportTo('tr.v', function() { |
* An important feature of the builder is that it's reusable, i.e. it can be |
* used to build multiple numerics with the same unit and bin structure. |
* |
- * @constructor |
- * @param {!tr.b.Unit} unit Unit of the resulting Histogram(s). |
- * @param {number} minBinBoundary The minimum boundary between bins, namely |
- * the underflow bin and the first central bin (or the overflow bin if |
- * no other boundaries are added later). |
*/ |
class HistogramBinBoundaries { |
/** |
@@ -603,46 +741,133 @@ tr.exportTo('tr.v', function() { |
} |
/** |
- * @param {number} minBinBoundary |
+ * @param {number} minBinBoundary The minimum boundary between bins, namely |
+ * the underflow bin and the first central bin (or the overflow bin if |
+ * no other boundaries are added later). |
*/ |
constructor(minBinBoundary) { |
- this.boundaries_ = [minBinBoundary]; |
+ this.boundaries_ = undefined; |
+ this.builder_ = [minBinBoundary]; |
+ this.range_ = new tr.b.Range(); |
+ this.range_.addValue(minBinBoundary); |
} |
- get minBinBoundary() { |
- return this.boundaries_[0]; |
+ get range() { |
+ return this.range_; |
} |
- get maxBinBoundary() { |
- return this.boundaries_[this.boundaries_.length - 1]; |
+ asDict() { |
+ // Copy builder_ in case ours is modified later. |
+ return this.builder_.slice(); |
+ } |
+ |
+ static fromDict(dict) { |
+ // When loading a results2.html with many Histograms with the same bin |
+ // boundaries, caching the HistogramBinBoundaries not only speeds up |
+ // loading, but also prevents a bug where build_ is occasionally |
+ // non-deterministic, which causes similar Histograms to be unmergeable. |
+ var cacheKey = JSON.stringify(dict); |
+ if (HISTOGRAM_BIN_BOUNDARIES_CACHE.has(cacheKey)) { |
+ return HISTOGRAM_BIN_BOUNDARIES_CACHE.get(cacheKey); |
+ } |
+ |
+ var binBoundaries = new HistogramBinBoundaries(dict[0]); |
+ for (var slice of dict.slice(1)) { |
+ if (!(slice instanceof Array)) { |
+ binBoundaries.addBinBoundary(slice); |
+ continue; |
+ } |
+ switch (slice[0]) { |
+ case HistogramBinBoundaries.SLICE_TYPE.LINEAR: |
+ binBoundaries.addLinearBins(slice[1], slice[2]); |
+ break; |
+ |
+ case HistogramBinBoundaries.SLICE_TYPE.EXPONENTIAL: |
+ binBoundaries.addExponentialBins(slice[1], slice[2]); |
+ break; |
+ |
+ default: |
+ throw new Error('Unrecognized HistogramBinBoundaries slice type'); |
+ } |
+ } |
+ HISTOGRAM_BIN_BOUNDARIES_CACHE.set(cacheKey, binBoundaries); |
+ return binBoundaries; |
} |
/** |
* Yield Ranges of adjacent boundaries. |
*/ |
- *[Symbol.iterator]() { |
+ *binRanges() { |
+ if (this.boundaries_ === undefined) { |
+ this.build_(); |
+ } |
for (var i = 0; i < this.boundaries_.length - 1; ++i) { |
yield tr.b.Range.fromExplicitRange( |
this.boundaries_[i], this.boundaries_[i + 1]); |
} |
} |
+ build_() { |
+ if (typeof this.builder_[0] !== 'number') { |
+ throw new Error('Invalid start of builder_'); |
+ } |
+ this.boundaries_ = [this.builder_[0]]; |
+ |
+ for (var slice of this.builder_.slice(1)) { |
+ if (!(slice instanceof Array)) { |
+ this.boundaries_.push(slice); |
+ continue; |
+ } |
+ var nextMaxBinBoundary = slice[1]; |
+ var binCount = slice[2]; |
+ var curMaxBinBoundary = this.boundaries_[ |
+ this.boundaries_.length - 1]; |
+ |
+ switch (slice[0]) { |
+ case HistogramBinBoundaries.SLICE_TYPE.LINEAR: |
+ var binWidth = (nextMaxBinBoundary - curMaxBinBoundary) / binCount; |
+ for (var i = 1; i < binCount; i++) { |
+ var boundary = curMaxBinBoundary + i * binWidth; |
+ this.boundaries_.push(boundary); |
+ } |
+ break; |
+ |
+ case HistogramBinBoundaries.SLICE_TYPE.EXPONENTIAL: |
+ var binExponentWidth = |
+ Math.log(nextMaxBinBoundary / curMaxBinBoundary) / binCount; |
+ for (var i = 1; i < binCount; i++) { |
+ var boundary = curMaxBinBoundary * Math.exp(i * binExponentWidth) |
+ this.boundaries_.push(boundary); |
+ } |
+ break; |
+ |
+ default: |
+ throw new Error('Unrecognized HistogramBinBoundaries slice type'); |
+ } |
+ this.boundaries_.push(nextMaxBinBoundary); |
+ } |
+ } |
+ |
/** |
* Add a bin boundary |nextMaxBinBoundary| to the builder. |
* |
* This operation effectively corresponds to appending a new central bin |
- * with the range [this.maxBinBoundary*, nextMaxBinBoundary]. |
+ * with the range [this.range.max, nextMaxBinBoundary]. |
* |
* @param {number} nextMaxBinBoundary The added bin boundary (must be |
* greater than |this.maxMinBoundary|). |
*/ |
addBinBoundary(nextMaxBinBoundary) { |
- if (nextMaxBinBoundary <= this.maxBinBoundary) { |
+ if (nextMaxBinBoundary <= this.range.max) { |
throw new Error('The added max bin boundary must be larger than ' + |
'the current max boundary'); |
} |
- this.boundaries_.push(nextMaxBinBoundary); |
+ // If boundaries_ had been built, then clear them. |
+ this.boundaries_ = undefined; |
+ |
+ this.builder_.push(nextMaxBinBoundary); |
+ this.range.addValue(nextMaxBinBoundary); |
return this; |
} |
@@ -652,7 +877,7 @@ tr.exportTo('tr.v', function() { |
* |
* This operation corresponds to appending |binCount| central bins of |
* constant range width |
- * W = ((|nextMaxBinBoundary| - |this.maxBinBoundary|) / |binCount|) |
+ * W = ((|nextMaxBinBoundary| - |this.range.max|) / |binCount|) |
* with the following ranges: |
* |
* [|this.maxMinBoundary|, |this.maxMinBoundary| + W] |
@@ -672,17 +897,17 @@ tr.exportTo('tr.v', function() { |
if (binCount <= 0) |
throw new Error('Bin count must be positive'); |
- var curMaxBinBoundary = this.maxBinBoundary; |
- if (curMaxBinBoundary >= nextMaxBinBoundary) { |
+ if (nextMaxBinBoundary <= this.range.max) { |
throw new Error('The new max bin boundary must be greater than ' + |
'the previous max bin boundary'); |
} |
- var binWidth = (nextMaxBinBoundary - curMaxBinBoundary) / binCount; |
- for (var i = 1; i < binCount; i++) |
- this.addBinBoundary(curMaxBinBoundary + i * binWidth); |
- this.addBinBoundary(nextMaxBinBoundary); |
+ // If boundaries_ had been built, then clear them. |
+ this.boundaries_ = undefined; |
+ this.builder_.push([HistogramBinBoundaries.SLICE_TYPE.LINEAR, |
+ nextMaxBinBoundary, binCount]); |
+ this.range.addValue(nextMaxBinBoundary); |
return this; |
} |
@@ -692,7 +917,7 @@ tr.exportTo('tr.v', function() { |
* |
* This operation corresponds to appending |binCount| central bins with |
* a constant difference between the logarithms of their range min and max |
- * D = ((ln(|nextMaxBinBoundary|) - ln(|this.maxBinBoundary|)) / |binCount|) |
+ * D = ((ln(|nextMaxBinBoundary|) - ln(|this.range.max|)) / |binCount|) |
* with the following ranges: |
* |
* [|this.maxMinBoundary|, |this.maxMinBoundary| * exp(D)] |
@@ -711,29 +936,32 @@ tr.exportTo('tr.v', function() { |
* @param {number} binCount Number of bins to be added (must be positive). |
*/ |
addExponentialBins(nextMaxBinBoundary, binCount) { |
- if (binCount <= 0) |
+ if (binCount <= 0) { |
throw new Error('Bin count must be positive'); |
- |
- var curMaxBinBoundary = this.maxBinBoundary; |
- if (curMaxBinBoundary <= 0) |
+ } |
+ if (this.range.max <= 0) { |
throw new Error('Current max bin boundary must be positive'); |
- if (curMaxBinBoundary >= nextMaxBinBoundary) { |
+ } |
+ if (this.range.max >= nextMaxBinBoundary) { |
throw new Error('The last added max boundary must be greater than ' + |
'the current max boundary boundary'); |
} |
- var binExponentWidth = |
- Math.log(nextMaxBinBoundary / curMaxBinBoundary) / binCount; |
- for (var i = 1; i < binCount; i++) { |
- this.addBinBoundary( |
- curMaxBinBoundary * Math.exp(i * binExponentWidth)); |
- } |
- this.addBinBoundary(nextMaxBinBoundary); |
+ // If boundaries_ had been built, then clear them. |
+ this.boundaries_ = undefined; |
+ this.builder_.push([HistogramBinBoundaries.SLICE_TYPE.EXPONENTIAL, |
+ nextMaxBinBoundary, binCount]); |
+ this.range.addValue(nextMaxBinBoundary); |
return this; |
} |
} |
+ HistogramBinBoundaries.SLICE_TYPE = { |
+ LINEAR: 0, |
+ EXPONENTIAL: 1, |
+ }; |
+ |
DEFAULT_BOUNDARIES_FOR_UNIT.set( |
tr.b.Unit.byName.timeDurationInMs.unitName, |
HistogramBinBoundaries.createExponential(1e-3, 1e6, 1e2)); |