44 NS_PER_SEC ,
55 targetPairwiseComparisonIntervalHalfWidth ,
66} from './config.js' ;
7- import type { BenchmarkResult } from './types.js' ;
7+ import type { BenchmarkResult , PairedComparison } from './types.js' ;
88
99// T-Distribution two-tailed critical values for 95% confidence.
1010// See http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm.
@@ -18,29 +18,67 @@ const tTable: { [v: number]: number } = {
1818} ;
1919const tTableInfinity = 1.96 ;
2020
21+ interface LogRatioStats {
22+ meanRatio : number ;
23+ lowRatio : number ;
24+ highRatio : number ;
25+ numSamples : number ;
26+ }
27+
2128// Computes stats on benchmark results.
2229export function computeStats (
2330 name : string ,
2431 timingSamples : ReadonlyArray < number > ,
2532 memorySamples : ReadonlyArray < number > ,
2633) : BenchmarkResult {
27- const { mean, marginOfError } = computeMeanStats ( timingSamples ) ;
28-
29- let meanMemUsed = 0 ;
30- for ( const memUsed of memorySamples ) {
31- meanMemUsed += memUsed ;
32- }
33- meanMemUsed /= memorySamples . length ;
34+ const { mean } = computeMeanStats ( timingSamples ) ;
3435
3536 return {
3637 name,
37- memPerOp : Math . floor ( meanMemUsed ) ,
38+ memPerOp : Math . floor ( computeMean ( memorySamples ) ) ,
3839 ops : NS_PER_SEC / mean ,
39- deviation : ( marginOfError / mean ) * 100 || 0 ,
40+ deviation : computeRelativeMarginOfError ( timingSamples ) ,
4041 numSamples : timingSamples . length ,
4142 } ;
4243}
4344
45+ export function getPairedComparisons (
46+ revisions : ReadonlyArray < string > ,
47+ timingSamplesByRevision : ReadonlyArray < ReadonlyArray < number > > ,
48+ ) : Array < PairedComparison > {
49+ const pairedComparisons : Array < PairedComparison > = [ ] ;
50+
51+ for (
52+ let baselineIndex = 1 ;
53+ baselineIndex < timingSamplesByRevision . length ;
54+ ++ baselineIndex
55+ ) {
56+ const baselineSamples = timingSamplesByRevision [ baselineIndex ] ;
57+
58+ for (
59+ let revisionIndex = 0 ;
60+ revisionIndex < baselineIndex ;
61+ ++ revisionIndex
62+ ) {
63+ const paired = computePairedComparison (
64+ baselineSamples ,
65+ timingSamplesByRevision [ revisionIndex ] ,
66+ ) ;
67+ if ( paired == null ) {
68+ continue ;
69+ }
70+
71+ pairedComparisons . push ( {
72+ baselineRevision : revisions [ baselineIndex ] ,
73+ revision : revisions [ revisionIndex ] ,
74+ ...paired ,
75+ } ) ;
76+ }
77+ }
78+
79+ return pairedComparisons ;
80+ }
81+
4482export function havePairwiseComparisonsStabilized (
4583 timingSamplesByRevision : ReadonlyArray < ReadonlyArray < number > > ,
4684) : boolean {
@@ -56,15 +94,13 @@ export function havePairwiseComparisonsStabilized(
5694 revisionIndex < baselineIndex ;
5795 ++ revisionIndex
5896 ) {
59- const ciHalfWidthPercent = computeLogRatioRelativeMarginOfError (
60- getRoundLogRatios (
61- baselineSamples ,
62- timingSamplesByRevision [ revisionIndex ] ,
63- ) ,
97+ const paired = computePairedComparison (
98+ baselineSamples ,
99+ timingSamplesByRevision [ revisionIndex ] ,
64100 ) ;
65101 if (
66- ciHalfWidthPercent == null ||
67- ciHalfWidthPercent > targetPairwiseComparisonIntervalHalfWidth
102+ paired == null ||
103+ paired . ciHalfWidthPercent > targetPairwiseComparisonIntervalHalfWidth
68104 ) {
69105 return false ;
70106 }
@@ -74,11 +110,52 @@ export function havePairwiseComparisonsStabilized(
74110 return true ;
75111}
76112
77- function computeLogRatioRelativeMarginOfError (
113+ function computeRelativeMarginOfError ( samples : ReadonlyArray < number > ) : number {
114+ const { mean, marginOfError } = computeMeanStats ( samples ) ;
115+ return ( marginOfError / mean ) * 100 || 0 ;
116+ }
117+
118+ function computeLogRatioStats (
78119 logRatios : ReadonlyArray < number > ,
79- ) : number | undefined {
80- const { marginOfError } = computeMeanStats ( logRatios ) ;
81- return Math . expm1 ( marginOfError ) * 100 ;
120+ ) : LogRatioStats | undefined {
121+ if ( logRatios . length < 2 ) {
122+ return ;
123+ }
124+
125+ const { mean, marginOfError } = computeMeanStats ( logRatios ) ;
126+ return {
127+ meanRatio : Math . exp ( mean ) ,
128+ lowRatio : Math . exp ( mean - marginOfError ) ,
129+ highRatio : Math . exp ( mean + marginOfError ) ,
130+ numSamples : logRatios . length ,
131+ } ;
132+ }
133+
134+ function computePairedComparison (
135+ baselineSamples : ReadonlyArray < number > ,
136+ samples : ReadonlyArray < number > ,
137+ ) : Omit < PairedComparison , 'baselineRevision' | 'revision' > | undefined {
138+ const logRatioStats = computeLogRatioStats (
139+ getRoundLogRatios ( baselineSamples , samples ) ,
140+ ) ;
141+ if ( logRatioStats == null ) {
142+ return ;
143+ }
144+
145+ const speedupPercent = ( logRatioStats . meanRatio - 1 ) * 100 ;
146+ const ciLowPercent = ( logRatioStats . lowRatio - 1 ) * 100 ;
147+ const ciHighPercent = ( logRatioStats . highRatio - 1 ) * 100 ;
148+
149+ return {
150+ speedupPercent,
151+ ciLowPercent,
152+ ciHighPercent,
153+ ciHalfWidthPercent : Math . max (
154+ Math . abs ( speedupPercent - ciLowPercent ) ,
155+ Math . abs ( ciHighPercent - speedupPercent ) ,
156+ ) ,
157+ numPairs : logRatioStats . numSamples ,
158+ } ;
82159}
83160
84161function getRoundLogRatios (
@@ -94,17 +171,21 @@ function getRoundLogRatios(
94171 return logRatios ;
95172}
96173
174+ function computeMean ( samples : ReadonlyArray < number > ) : number {
175+ let mean = 0 ;
176+ for ( const sample of samples ) {
177+ mean += sample ;
178+ }
179+ return mean / samples . length ;
180+ }
181+
97182function computeMeanStats ( samples : ReadonlyArray < number > ) : {
98183 mean : number ;
99184 marginOfError : number ;
100185} {
101186 assert ( samples . length > 1 ) ;
102187
103- let mean = 0 ;
104- for ( const sample of samples ) {
105- mean += sample ;
106- }
107- mean /= samples . length ;
188+ const mean = computeMean ( samples ) ;
108189
109190 let variance = 0 ;
110191 for ( const sample of samples ) {
0 commit comments