3 sampleMean: function(numberOfSamples, sum)
5 if (numberOfSamples < 1)
7 return sum / numberOfSamples;
10 // With sum and sum of squares, we can compute the sample standard deviation in O(1).
11 // See https://rniwa.com/2012-11-10/sample-standard-deviation-in-terms-of-sum-and-square-sum-of-samples/
12 unbiasedSampleStandardDeviation: function(numberOfSamples, sum, squareSum)
14 if (numberOfSamples < 2)
16 return Math.sqrt((squareSum - sum * sum / numberOfSamples) / (numberOfSamples - 1));
19 geometricMean: function(values)
23 var roots = values.map(function(value) { return Math.pow(value, 1 / values.length); })
24 return roots.reduce(function(a, b) { return a * b; });
31 this._maxHeap = Algorithm.createMaxHeap(Experiment.defaults.CONCERN_SIZE);
40 Experiment.prototype =
46 this._numberOfSamples = 0;
49 // Called after a warm-up period
50 startSampling: function()
52 var mean = this.mean();
58 sample: function(value)
61 this._squareSum += value * value;
62 this._maxHeap.push(value);
63 ++this._numberOfSamples;
68 return Statistics.sampleMean(this._numberOfSamples, this._sum);
71 standardDeviation: function()
73 return Statistics.unbiasedSampleStandardDeviation(this._numberOfSamples, this._sum, this._squareSum);
76 percentage: function()
78 var mean = this.mean();
79 return mean ? this.standardDeviation() * 100 / mean : 0;
82 concern: function(percentage)
84 var size = Math.ceil(this._numberOfSamples * percentage / 100);
85 var values = this._maxHeap.values(size);
86 return values.length ? values.reduce(function(a, b) { return a + b; }) / values.length : 0;
89 score: function(percentage)
91 return Statistics.geometricMean([this.mean(), Math.max(this.concern(percentage), 1)]);
95 function Sampler(count)
97 this.experiments = [];
99 this.experiments.push(new Experiment());
101 this.samplingTimeOffset = 0;
106 startSampling: function(samplingTimeOffset)
108 this.experiments.forEach(function(experiment) {
109 experiment.startSampling();
112 this.samplingTimeOffset = samplingTimeOffset / 1000;
115 sample: function(timeOffset, values)
117 if (values.length < this.experiments.length)
118 throw "Not enough sample points";
120 this.experiments.forEach(function(experiment, index) {
121 experiment.sample(values[index]);
124 this.samples.push({ timeOffset: timeOffset / 1000, values: values });
127 toJSON: function(statistics, graph)
131 results[Strings["JSON_SCORE"]] = this.experiments[0].score(Experiment.defaults.CONCERN);
134 this.experiments.forEach(function(experiment, index) {
135 results[Strings["JSON_EXPERIMENTS"][index]] = {};
136 results[Strings["JSON_EXPERIMENTS"][index]][Strings["JSON_MEASUREMENTS"][0]] = experiment.mean();
137 results[Strings["JSON_EXPERIMENTS"][index]][Strings["JSON_MEASUREMENTS"][1]] = experiment.concern(Experiment.defaults.CONCERN);
138 results[Strings["JSON_EXPERIMENTS"][index]][Strings["JSON_MEASUREMENTS"][2]] = experiment.standardDeviation();
139 results[Strings["JSON_EXPERIMENTS"][index]][Strings["JSON_MEASUREMENTS"][3]] = experiment.percentage();
144 results[Strings["JSON_SAMPLES"][0]] = {};
145 results[Strings["JSON_SAMPLES"][0]][Strings["JSON_GRAPH"][0]] = this.samples;
146 results[Strings["JSON_SAMPLES"][0]][Strings["JSON_GRAPH"][1]] = this.samplingTimeOffset;