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

Side by Side Diff: tracing/tracing/value/histogram.html

Issue 2293533002: Refactor NumericBuilder to HistogramBinBoundaries. (Closed) Base URL: https://github.com/catapult-project/catapult.git@master
Patch Set: rebase Created 4 years, 3 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 <!DOCTYPE html> 1 <!DOCTYPE html>
2 <!-- 2 <!--
3 Copyright 2016 The Chromium Authors. All rights reserved. 3 Copyright 2016 The Chromium Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style license that can be 4 Use of this source code is governed by a BSD-style license that can be
5 found in the LICENSE file. 5 found in the LICENSE file.
6 --> 6 -->
7 7
8 <link rel="import" href="/tracing/base/iteration_helpers.html"> 8 <link rel="import" href="/tracing/base/iteration_helpers.html">
9 <link rel="import" href="/tracing/base/range.html"> 9 <link rel="import" href="/tracing/base/range.html">
10 <link rel="import" href="/tracing/base/running_statistics.html"> 10 <link rel="import" href="/tracing/base/running_statistics.html">
(...skipping 12 matching lines...) Expand all
23 // p-values less than this indicate statistical significance. 23 // p-values less than this indicate statistical significance.
24 var DEFAULT_ALPHA = 0.05; 24 var DEFAULT_ALPHA = 0.05;
25 25
26 /** @enum */ 26 /** @enum */
27 var Significance = { 27 var Significance = {
28 DONT_CARE: -1, 28 DONT_CARE: -1,
29 INSIGNIFICANT: 0, 29 INSIGNIFICANT: 0,
30 SIGNIFICANT: 1 30 SIGNIFICANT: 1
31 }; 31 };
32 32
33 var DEFAULT_BUILDER_FOR_UNIT = new Map(); 33 var DEFAULT_BOUNDARIES_FOR_UNIT = new Map();
34 34
35 class HistogramBin { 35 class HistogramBin {
36 constructor(parentNumeric, min, max) { 36 /**
37 this.parentNumeric = parentNumeric; 37 * @param {!tr.b.Range} range
38 this.range = tr.b.Range.fromExplicitRange(min, max); 38 */
39 constructor(range) {
40 this.range = range;
39 this.count = 0; 41 this.count = 0;
40 this.diagnosticMaps = []; 42 this.diagnosticMaps = [];
41 } 43 }
42 44
43 /** 45 /**
44 * @param {*} value 46 * @param {*} value
45 * @param {!tr.v.d.DiagnosticMap=} opt_diagnosticMap 47 * @param {!tr.v.d.DiagnosticMap=} opt_diagnosticMap
46 */ 48 */
47 add(value, opt_diagnosticMap) { 49 add(value, opt_diagnosticMap) {
48 if ((opt_diagnosticMap !== undefined) && 50 if ((opt_diagnosticMap !== undefined) &&
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
86 * This is basically a histogram, but so much more. 88 * This is basically a histogram, but so much more.
87 * Histogram is serializable using asDict/fromDict. 89 * Histogram is serializable using asDict/fromDict.
88 * Histogram computes several statistics of its contents. 90 * Histogram computes several statistics of its contents.
89 * Histograms can be merged. 91 * Histograms can be merged.
90 * getDifferenceSignificance() test whether one Histogram is statistically 92 * getDifferenceSignificance() test whether one Histogram is statistically
91 * significantly different from another Histogram. 93 * significantly different from another Histogram.
92 * Histogram stores a random sample of the exact number values added to it. 94 * Histogram stores a random sample of the exact number values added to it.
93 * Histogram stores a random sample of optional per-sample DiagnosticMaps. 95 * Histogram stores a random sample of optional per-sample DiagnosticMaps.
94 * Histogram is visualized by <tr-v-ui-histogram-span>, which supports 96 * Histogram is visualized by <tr-v-ui-histogram-span>, which supports
95 * selecting bins, and visualizing the DiagnosticMaps of selected bins. 97 * selecting bins, and visualizing the DiagnosticMaps of selected bins.
98 *
99 * @param {!tr.v.Unit} unit
100 * @param {!tr.v.HistogramBinBoundaries=} opt_binBoundaries
96 */ 101 */
97 class Histogram extends tr.v.NumericBase { 102 class Histogram extends tr.v.NumericBase {
98 constructor(unit, opt_binBounds) { 103 constructor(unit, opt_binBoundaries) {
99 super(unit); 104 super(unit);
100 105
101 var binBounds = opt_binBounds; 106 var binBoundaries = opt_binBoundaries;
102 if (!binBounds) { 107 if (!binBoundaries) {
103 var baseUnit = unit.baseUnit ? unit.baseUnit : unit; 108 var baseUnit = unit.baseUnit ? unit.baseUnit : unit;
104 binBounds = DEFAULT_BUILDER_FOR_UNIT.get(baseUnit.unitName).binBounds; 109 binBoundaries = DEFAULT_BOUNDARIES_FOR_UNIT.get(baseUnit.unitName);
105 } 110 }
106 if (binBounds.length < 2)
107 throw new Error('Need at least 2 binBounds');
108 111
109 this.allBins = []; 112 this.allBins = [];
110 this.centralBins = []; 113 this.centralBins = [];
111 this.maxCount_ = 0; 114 this.maxCount_ = 0;
112 this.nanDiagnosticMaps = []; 115 this.nanDiagnosticMaps = [];
113 this.numNans = 0; 116 this.numNans = 0;
114 this.running = new tr.b.RunningStatistics(); 117 this.running = new tr.b.RunningStatistics();
115 this.sampleValues_ = []; 118 this.sampleValues_ = [];
116 this.summaryOptions = { 119 this.summaryOptions = {
117 count: true, 120 count: true,
118 sum: true, 121 sum: true,
119 avg: true, 122 avg: true,
120 std: true, 123 std: true,
121 min: true, 124 min: true,
122 max: true, 125 max: true,
123 nans: false, 126 nans: false,
124 percentile: [] 127 percentile: []
125 }; 128 };
126 129
127 this.underflowBin = new HistogramBin(this, 130 this.underflowBin = new HistogramBin(tr.b.Range.fromExplicitRange(
128 -Number.MAX_VALUE, binBounds[0]); 131 -Number.MAX_VALUE, binBoundaries.minBinBoundary));
129 this.overflowBin = new HistogramBin(this, 132 this.overflowBin = new HistogramBin(tr.b.Range.fromExplicitRange(
130 binBounds[binBounds.length - 1], Number.MAX_VALUE); 133 binBoundaries.maxBinBoundary, Number.MAX_VALUE));
131 134
132 for (var i = 0; i < binBounds.length - 1; ++i) { 135 for (var range of binBoundaries)
133 this.centralBins.push(new HistogramBin( 136 this.centralBins.push(new HistogramBin(range));
134 this, binBounds[i], binBounds[i + 1]));
135 }
136 137
137 this.allBins.push(this.underflowBin); 138 this.allBins.push(this.underflowBin);
138 for (var bin of this.centralBins) 139 for (var bin of this.centralBins)
139 this.allBins.push(bin); 140 this.allBins.push(bin);
140 this.allBins.push(this.overflowBin); 141 this.allBins.push(this.overflowBin);
141 142
142 this.maxNumSampleValues = this.allBins.length * 10; 143 this.maxNumSampleValues = this.allBins.length * 10;
143 } 144 }
144 145
145 static fromDict(d) { 146 static fromDict(d) {
146 var n = new Histogram(tr.v.Unit.fromJSON(d.unit), d.binBounds); 147 var boundaries = HistogramBinBoundaries.createWithBoundaries(
148 d.binBoundaries);
149 var n = new Histogram(tr.v.Unit.fromJSON(d.unit), boundaries);
147 n.underflowBin.fromDict(d.underflowBin); 150 n.underflowBin.fromDict(d.underflowBin);
148 for (var i = 0; i < d.centralBins.length; ++i) 151 for (var i = 0; i < d.centralBins.length; ++i)
149 n.centralBins[i].fromDict(d.centralBins[i]); 152 n.centralBins[i].fromDict(d.centralBins[i]);
150 n.overflowBin.fromDict(d.overflowBin); 153 n.overflowBin.fromDict(d.overflowBin);
151 154
152 for (var bin of n.allBins) 155 for (var bin of n.allBins)
153 n.maxCount_ = Math.max(n.maxCount_, bin.count); 156 n.maxCount_ = Math.max(n.maxCount_, bin.count);
154 157
155 if (d.running) 158 if (d.running)
156 n.running = tr.b.RunningStatistics.fromDict(d.running); 159 n.running = tr.b.RunningStatistics.fromDict(d.running);
(...skipping 15 matching lines...) Expand all
172 * set of ScalarNumerics. 175 * set of ScalarNumerics.
173 * The range of the resulting histogram is determined by the smallest and 176 * The range of the resulting histogram is determined by the smallest and
174 * largest sample value, which is unpredictable. 177 * largest sample value, which is unpredictable.
175 * https://github.com/catapult-project/catapult/issues/2685 178 * https://github.com/catapult-project/catapult/issues/2685
176 * 179 *
177 * @param {!tr.v.Unit} unit 180 * @param {!tr.v.Unit} unit
178 * @param {!Array.<number>} samples 181 * @param {!Array.<number>} samples
179 * @return {!Histogram} 182 * @return {!Histogram}
180 */ 183 */
181 static buildFromSamples(unit, samples) { 184 static buildFromSamples(unit, samples) {
182 var result = NumericBuilder.createFromSamples(unit, samples).build(); 185 var boundaries = HistogramBinBoundaries.createFromSamples(samples);
186 var result = new Histogram(unit, boundaries);
183 result.maxNumSampleValues = 1000; 187 result.maxNumSampleValues = 1000;
184 188
185 // TODO(eakuefner): Propagate diagnosticMaps? 189 // TODO(eakuefner): Propagate diagnosticMaps?
186 for (var sample of samples) 190 for (var sample of samples)
187 result.add(sample); 191 result.add(sample);
188 192
189 return result; 193 return result;
190 } 194 }
191 195
192 get numValues() { 196 get numValues() {
(...skipping 241 matching lines...) Expand 10 before | Expand all | Expand 10 after
434 } 438 }
435 } 439 }
436 }, this); 440 }, this);
437 return results; 441 return results;
438 } 442 }
439 443
440 get sampleValues() { 444 get sampleValues() {
441 return this.sampleValues_; 445 return this.sampleValues_;
442 } 446 }
443 447
444 get binBounds() { 448 get binBoundaries() {
445 var bounds = []; 449 var boundaries = [];
446 for (var bin of this.centralBins) 450 for (var bin of this.centralBins)
447 bounds.push(bin.range.min); 451 boundaries.push(bin.range.min);
448 bounds.push(this.overflowBin.range.min); 452 boundaries.push(this.overflowBin.range.min);
449 return bounds; 453 return boundaries;
450 } 454 }
451 455
452 clone() { 456 clone() {
453 return Histogram.fromDict(this.asDict()); 457 return Histogram.fromDict(this.asDict());
454 } 458 }
455 459
456 asDict() { 460 asDict() {
457 return { 461 return {
458 type: 'numeric', 462 type: 'numeric',
459 unit: this.unit.asJSON(), 463 unit: this.unit.asJSON(),
460 binBounds: this.binBounds, 464 binBoundaries: this.binBoundaries,
461 465
462 underflowBin: this.underflowBin.asDict(), 466 underflowBin: this.underflowBin.asDict(),
463 centralBins: this.centralBins.map((bin) => bin.asDict()), 467 centralBins: this.centralBins.map(bin => bin.asDict()),
464 overflowBin: this.overflowBin.asDict(), 468 overflowBin: this.overflowBin.asDict(),
465 469
466 running: this.running.asDict(), 470 running: this.running.asDict(),
467 summaryOptions: this.summaryOptions, 471 summaryOptions: this.summaryOptions,
468 472
469 maxNumSampleValues: this.maxNumSampleValues, 473 maxNumSampleValues: this.maxNumSampleValues,
470 sampleValues: this.sampleValues, 474 sampleValues: this.sampleValues,
471 475
472 numNans: this.numNans, 476 numNans: this.numNans,
473 nanDiagnosticMaps: this.nanDiagnosticMaps.map(dm => dm.asDict()), 477 nanDiagnosticMaps: this.nanDiagnosticMaps.map(dm => dm.asDict()),
(...skipping 28 matching lines...) Expand all
502 * 506 *
503 * An important feature of the builder is that it's reusable, i.e. it can be 507 * An important feature of the builder is that it's reusable, i.e. it can be
504 * used to build multiple numerics with the same unit and bin structure. 508 * used to build multiple numerics with the same unit and bin structure.
505 * 509 *
506 * @constructor 510 * @constructor
507 * @param {!tr.v.Unit} unit Unit of the resulting Histogram(s). 511 * @param {!tr.v.Unit} unit Unit of the resulting Histogram(s).
508 * @param {number} minBinBoundary The minimum boundary between bins, namely 512 * @param {number} minBinBoundary The minimum boundary between bins, namely
509 * the underflow bin and the first central bin (or the overflow bin if 513 * the underflow bin and the first central bin (or the overflow bin if
510 * no other boundaries are added later). 514 * no other boundaries are added later).
511 */ 515 */
512 class NumericBuilder { 516 class HistogramBinBoundaries {
513 /** 517 /**
514 * Create a linearly scaled tr.v.NumericBuilder with |numBins| bins ranging 518 * Create a linearly scaled tr.v.HistogramBinBoundaries with |numBins| bins
515 * from |range.min| to |range.max|. 519 * ranging from |min| to |max|.
516 * 520 *
517 * @param {!tr.v.Unit} unit 521 * @param {number} min
518 * @param {!tr.b.Range} range 522 * @param {number} max
519 * @param {number} numBins 523 * @param {number} numBins
520 * @return {tr.v.NumericBuilder} 524 * @return {tr.v.HistogramBinBoundaries}
521 */ 525 */
522 static createLinear(unit, range, numBins) { 526 static createLinear(min, max, numBins) {
523 if (range.isEmpty) 527 return new HistogramBinBoundaries(min).addLinearBins(max, numBins);
524 throw new Error('Range must be non-empty');
525
526 return new NumericBuilder(unit, range.min).addLinearBins(
527 range.max, numBins);
528 } 528 }
529 529
530 /** 530 /**
531 * Create an exponentially scaled tr.v.NumericBuilder with |numBins| bins 531 * Create an exponentially scaled tr.v.HistogramBinBoundaries with |numBins|
532 * ranging from |range.min| to |range.max|. 532 * bins ranging from |min| to |max|.
533 * 533 *
534 * @param {!tr.v.Unit} unit 534 * @param {number} min
535 * @param {!tr.b.Range} range 535 * @param {number} max
536 * @param {number} numBins 536 * @param {number} numBins
537 * @return {tr.v.NumericBuilder} 537 * @return {tr.v.HistogramBinBoundaries}
538 */ 538 */
539 static createExponential(unit, range, numBins) { 539 static createExponential(min, max, numBins) {
540 if (range.isEmpty) 540 return new HistogramBinBoundaries(min).addExponentialBins(max, numBins);
541 throw new Error('Range must be non-empty');
542 return new NumericBuilder(unit, range.min).addExponentialBins(
543 range.max, numBins);
544 } 541 }
545 542
546 static createFromSamples(unit, samples) { 543 /**
544 * @param {Array.<number>} binBoundaries
545 */
546 static createWithBoundaries(binBoundaries) {
547 var builder = new HistogramBinBoundaries(binBoundaries[0]);
548 for (var boundary of binBoundaries.slice(1))
549 builder.addBinBoundary(boundary);
550 return builder;
551 }
552
553 static createFromSamples(samples) {
547 var range = new tr.b.Range(); 554 var range = new tr.b.Range();
548 // Prevent non-numeric samples from introducing NaNs into the range. 555 // Prevent non-numeric samples from introducing NaNs into the range.
549 for (var sample of samples) 556 for (var sample of samples)
550 if (!isNaN(Math.max(sample))) 557 if (!isNaN(Math.max(sample)))
551 range.addValue(sample); 558 range.addValue(sample);
552 559
553 // NumericBuilder.addLinearBins() requires this. 560 // HistogramBinBoundaries.addLinearBins() requires this.
554 if (range.isEmpty) 561 if (range.isEmpty)
555 range.addValue(1); 562 range.addValue(1);
556 if (range.min === range.max) 563 if (range.min === range.max)
557 range.addValue(range.min - 1); 564 range.addValue(range.min - 1);
558 565
559 // This optimizes the resolution when samples are uniformly distributed 566 // This optimizes the resolution when samples are uniformly distributed
560 // (which is almost never the case). 567 // (which is almost never the case).
561 var numBins = Math.ceil(Math.sqrt(samples.length)); 568 var numBins = Math.ceil(Math.sqrt(samples.length));
562 var builder = new NumericBuilder(unit, range.min); 569 var builder = new HistogramBinBoundaries(range.min);
563 builder.addLinearBins(range.max, numBins); 570 builder.addLinearBins(range.max, numBins);
564 return builder; 571 return builder;
565 } 572 }
566 573
567 constructor(unit, minBinBoundary) { 574 /**
568 this.unit_ = unit; 575 * @param {number} minBinBoundary
576 */
577 constructor(minBinBoundary) {
569 this.boundaries_ = [minBinBoundary]; 578 this.boundaries_ = [minBinBoundary];
570 } 579 }
571 580
572 get binBounds() {
573 return this.boundaries_;
574 }
575
576 get minBinBoundary() { 581 get minBinBoundary() {
577 return this.boundaries_[0]; 582 return this.boundaries_[0];
578 } 583 }
579 584
580 get maxBinBoundary() { 585 get maxBinBoundary() {
581 return this.boundaries_[this.boundaries_.length - 1]; 586 return this.boundaries_[this.boundaries_.length - 1];
582 } 587 }
583 588
584 /** 589 /**
590 * Yield Ranges of adjacent boundaries.
591 */
592 *[Symbol.iterator]() {
593 for (var i = 0; i < this.boundaries_.length - 1; ++i) {
594 yield tr.b.Range.fromExplicitRange(
595 this.boundaries_[i], this.boundaries_[i + 1]);
596 }
597 }
598
599 /**
585 * Add a bin boundary |nextMaxBinBoundary| to the builder. 600 * Add a bin boundary |nextMaxBinBoundary| to the builder.
586 * 601 *
587 * This operation effectively corresponds to appending a new central bin 602 * This operation effectively corresponds to appending a new central bin
588 * with the range [this.maxBinBoundary*, nextMaxBinBoundary]. 603 * with the range [this.maxBinBoundary*, nextMaxBinBoundary].
589 * 604 *
590 * @param {number} nextMaxBinBoundary The added bin boundary (must be 605 * @param {number} nextMaxBinBoundary The added bin boundary (must be
591 * greater than |this.maxMinBoundary|). 606 * greater than |this.maxMinBoundary|).
592 */ 607 */
593 addBinBoundary(nextMaxBinBoundary) { 608 addBinBoundary(nextMaxBinBoundary) {
594 if (nextMaxBinBoundary <= this.maxBinBoundary) { 609 if (nextMaxBinBoundary <= this.maxBinBoundary) {
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after
679 var binExponentWidth = 694 var binExponentWidth =
680 Math.log(nextMaxBinBoundary / curMaxBinBoundary) / binCount; 695 Math.log(nextMaxBinBoundary / curMaxBinBoundary) / binCount;
681 for (var i = 1; i < binCount; i++) { 696 for (var i = 1; i < binCount; i++) {
682 this.addBinBoundary( 697 this.addBinBoundary(
683 curMaxBinBoundary * Math.exp(i * binExponentWidth)); 698 curMaxBinBoundary * Math.exp(i * binExponentWidth));
684 } 699 }
685 this.addBinBoundary(nextMaxBinBoundary); 700 this.addBinBoundary(nextMaxBinBoundary);
686 701
687 return this; 702 return this;
688 } 703 }
689
690 /**
691 * Build a tr.v.Histogram from the list of bin boundaries.
692 *
693 * As explained earlier, this method can be called arbitrarily many times
694 * to produce arbitrarily many distinct numerics.
695 */
696 build() {
697 return new Histogram(this.unit_, this.boundaries_);
698 }
699 }; 704 };
700 705
701 function defineDefaultBuilder(unit, options) { 706 DEFAULT_BOUNDARIES_FOR_UNIT.set(
702 var range = tr.b.Range.fromExplicitRange(options.min, options.max); 707 tr.v.Unit.byName.timeDurationInMs.unitName,
703 var builder; 708 HistogramBinBoundaries.createExponential(1e-3, 1e6, 1e2));
704 if (options.exponential)
705 builder = NumericBuilder.createExponential(unit, range, options.numBins);
706 else
707 builder = NumericBuilder.createLinear(unit, range, options.numBins);
708 DEFAULT_BUILDER_FOR_UNIT.set(unit.unitName, builder);
709 }
710 709
711 defineDefaultBuilder(tr.v.Unit.byName.timeDurationInMs, { 710 DEFAULT_BOUNDARIES_FOR_UNIT.set(
712 exponential: true, min: 1e-3, max: 1e6, numBins: 1e2}); 711 tr.v.Unit.byName.timeStampInMs.unitName,
713 defineDefaultBuilder(tr.v.Unit.byName.timeStampInMs, { 712 HistogramBinBoundaries.createLinear(0, 1e10, 1e3));
714 exponential: false, min: 0, max: 1e10, numBins: 1e3}); 713
715 defineDefaultBuilder(tr.v.Unit.byName.normalizedPercentage, { 714 DEFAULT_BOUNDARIES_FOR_UNIT.set(
716 exponential: false, min: 0.0, max: 1.0, numBins: 20}); 715 tr.v.Unit.byName.normalizedPercentage.unitName,
717 defineDefaultBuilder(tr.v.Unit.byName.sizeInBytes, { 716 HistogramBinBoundaries.createLinear(0, 1.0, 20));
718 exponential: true, min: 1, max: 1e12, numBins: 1e2}); 717
719 defineDefaultBuilder(tr.v.Unit.byName.energyInJoules, { 718 DEFAULT_BOUNDARIES_FOR_UNIT.set(
720 exponential: true, min: 1e-3, max: 1e2, numBins: 30}); 719 tr.v.Unit.byName.sizeInBytes.unitName,
721 defineDefaultBuilder(tr.v.Unit.byName.powerInWatts, { 720 HistogramBinBoundaries.createExponential(1, 1e12, 1e2));
722 exponential: true, min: 1e-3, max: 1e2, numBins: 30}); 721
723 defineDefaultBuilder(tr.v.Unit.byName.unitlessNumber, { 722 DEFAULT_BOUNDARIES_FOR_UNIT.set(
724 exponential: true, min: 1e-3, max: 1e3, numBins: 20}); 723 tr.v.Unit.byName.energyInJoules.unitName,
725 defineDefaultBuilder(tr.v.Unit.byName.count, { 724 HistogramBinBoundaries.createExponential(1e-3, 1e3, 50));
726 exponential: true, min: 1, max: 1e3, numBins: 20}); 725
726 DEFAULT_BOUNDARIES_FOR_UNIT.set(
727 tr.v.Unit.byName.powerInWatts.unitName,
728 HistogramBinBoundaries.createExponential(1e-3, 1, 50));
729
730 DEFAULT_BOUNDARIES_FOR_UNIT.set(
731 tr.v.Unit.byName.unitlessNumber.unitName,
732 HistogramBinBoundaries.createExponential(1e-3, 1e3, 50));
733
734 DEFAULT_BOUNDARIES_FOR_UNIT.set(
735 tr.v.Unit.byName.count.unitName,
736 HistogramBinBoundaries.createExponential(1, 1e3, 20));
727 737
728 return { 738 return {
729 Significance: Significance, 739 Significance: Significance,
730 HistogramBin: HistogramBin,
731 Histogram: Histogram, 740 Histogram: Histogram,
732 NumericBuilder: NumericBuilder, 741 HistogramBinBoundaries: HistogramBinBoundaries,
733 }; 742 };
734 }); 743 });
735 </script> 744 </script>
OLDNEW
« no previous file with comments | « tracing/tracing/value/diagnostics/composition_test.html ('k') | tracing/tracing/value/histogram_test.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698