@@ -28,29 +28,44 @@ function aggregateNumbers(values) {
2828 standardDeviation : null ,
2929 spreadPercent : null ,
3030 sampleCount : 0 ,
31+ trimmedCount : 0 ,
3132 }
3233 }
3334
34- const middle = Math . floor ( sorted . length / 2 )
35+ // IQR-based outlier trimming (only when enough samples)
36+ let trimmed = sorted
37+ if ( sorted . length >= 8 ) {
38+ const q1Index = Math . floor ( sorted . length * 0.25 )
39+ const q3Index = Math . floor ( sorted . length * 0.75 )
40+ const q1 = sorted [ q1Index ]
41+ const q3 = sorted [ q3Index ]
42+ const iqr = q3 - q1
43+ const lowerFence = q1 - 1.5 * iqr
44+ const upperFence = q3 + 1.5 * iqr
45+ trimmed = sorted . filter ( ( value ) => value >= lowerFence && value <= upperFence )
46+ if ( trimmed . length < sorted . length * 0.5 ) {
47+ trimmed = sorted
48+ }
49+ }
50+
51+ const middle = Math . floor ( trimmed . length / 2 )
3552 const median =
36- sorted . length % 2 === 0
37- ? ( sorted [ middle - 1 ] + sorted [ middle ] ) / 2
38- : sorted [ middle ]
39- const mean = sorted . reduce ( ( total , value ) => total + value , 0 ) / sorted . length
40- const variance =
41- sorted . reduce ( ( total , value ) => total + ( value - mean ) ** 2 , 0 ) / sorted . length
53+ trimmed . length % 2 === 0 ? ( trimmed [ middle - 1 ] + trimmed [ middle ] ) / 2 : trimmed [ middle ]
54+ const mean = trimmed . reduce ( ( total , value ) => total + value , 0 ) / trimmed . length
55+ const variance = trimmed . reduce ( ( total , value ) => total + ( value - mean ) ** 2 , 0 ) / trimmed . length
4256 const standardDeviation = Math . sqrt ( variance )
43- const p95 = sorted [ Math . min ( sorted . length - 1 , Math . ceil ( sorted . length * 0.95 ) - 1 ) ]
57+ const p95 = trimmed [ Math . min ( trimmed . length - 1 , Math . ceil ( trimmed . length * 0.95 ) - 1 ) ]
4458
4559 return {
4660 median,
4761 p95,
48- min : sorted [ 0 ] ,
49- max : sorted [ sorted . length - 1 ] ,
62+ min : trimmed [ 0 ] ,
63+ max : trimmed [ trimmed . length - 1 ] ,
5064 mean,
5165 standardDeviation,
5266 spreadPercent : median === 0 ? null : ( ( p95 - median ) / Math . abs ( median ) ) * 100 ,
5367 sampleCount : sorted . length ,
68+ trimmedCount : trimmed . length ,
5469 }
5570}
5671
@@ -134,13 +149,13 @@ function buildMarkdownReport(result) {
134149 `- Generation filter: ${ result . generationFilter } ` ,
135150 `- Counts: ${ result . counts . join ( ', ' ) } ` ,
136151 '' ,
137- '| Count | V5 mount | V6 mount | Mount delta | Mount spread | V5 unmount | V6 unmount | Unmount delta | Unmount spread | V5 mount mem | V6 mount mem | Mount mem delta | Mount mem spread | V5 unmount mem | V6 unmount mem | Unmount mem delta | Unmount mem spread | Samples | V5 timeouts | V6 timeouts |' ,
138- '| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |' ,
152+ '| Count | V5 mount | V6 mount | Mount delta | Mount spread | V5 unmount | V6 unmount | Unmount delta | Unmount spread | V5 update | V6 update | Update delta | Update spread | V5 mount mem | V6 mount mem | Mount mem delta | Mount mem spread | V5 unmount mem | V6 unmount mem | Unmount mem delta | Unmount mem spread | Samples | V5 timeouts | V6 timeouts |' ,
153+ '| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ' ,
139154 ]
140155
141156 result . summary . forEach ( ( row ) => {
142157 lines . push (
143- `| ${ row . count } | ${ formatMs ( row . v5 . mount . median ) } | ${ formatMs ( row . v6 . mount . median ) } | ${ formatMs ( row . mountDeltaMs ) } | ${ formatPercent ( row . mountDeltaSpreadPercent ) } | ${ formatMs ( row . v5 . unmount . median ) } | ${ formatMs ( row . v6 . unmount . median ) } | ${ formatMs ( row . unmountDeltaMs ) } | ${ formatPercent ( row . unmountDeltaSpreadPercent ) } | ${ formatBytes ( row . v5 . mountMemory . median ) } | ${ formatBytes ( row . v6 . mountMemory . median ) } | ${ formatBytes ( row . mountMemoryDeltaBytes ) } | ${ formatPercent ( row . mountMemoryDeltaSpreadPercent ) } | ${ formatBytes ( row . v5 . unmountMemory . median ) } | ${ formatBytes ( row . v6 . unmountMemory . median ) } | ${ formatBytes ( row . unmountMemoryDeltaBytes ) } | ${ formatPercent ( row . unmountMemoryDeltaSpreadPercent ) } | ${ row . sampleCount } | ${ row . v5 . timeoutCount } | ${ row . v6 . timeoutCount } |` ,
158+ `| ${ row . count } | ${ formatMs ( row . v5 . mount . median ) } | ${ formatMs ( row . v6 . mount . median ) } | ${ formatMs ( row . mountDeltaMs ) } | ${ formatPercent ( row . mountDeltaSpreadPercent ) } | ${ formatMs ( row . v5 . unmount . median ) } | ${ formatMs ( row . v6 . unmount . median ) } | ${ formatMs ( row . unmountDeltaMs ) } | ${ formatPercent ( row . unmountDeltaSpreadPercent ) } | ${ formatMs ( row . v5 . update . median ) } | ${ formatMs ( row . v6 . update . median ) } | ${ formatMs ( row . updateDeltaMs ) } | ${ formatPercent ( row . updateDeltaSpreadPercent ) } | ${ formatBytes ( row . v5 . mountMemory . median ) } | ${ formatBytes ( row . v6 . mountMemory . median ) } | ${ formatBytes ( row . mountMemoryDeltaBytes ) } | ${ formatPercent ( row . mountMemoryDeltaSpreadPercent ) } | ${ formatBytes ( row . v5 . unmountMemory . median ) } | ${ formatBytes ( row . v6 . unmountMemory . median ) } | ${ formatBytes ( row . unmountMemoryDeltaBytes ) } | ${ formatPercent ( row . unmountMemoryDeltaSpreadPercent ) } | ${ row . sampleCount } | ${ row . v5 . timeoutCount } | ${ row . v6 . timeoutCount } |` ,
144159 )
145160 } )
146161
@@ -163,9 +178,9 @@ async function main() {
163178 throw new Error ( 'No benchmark result files matched the requested generation filter.' )
164179 }
165180
166- const counts = Array . from (
167- new Set ( runs . flatMap ( ( run ) => run . counts ?? [ ] ) ) ,
168- ) . sort ( ( left , right ) => left - right )
181+ const counts = Array . from ( new Set ( runs . flatMap ( ( run ) => run . counts ?? [ ] ) ) ) . sort (
182+ ( left , right ) => left - right ,
183+ )
169184
170185 const summary = counts . map ( ( count ) => {
171186 const rows = runs
@@ -175,12 +190,10 @@ async function main() {
175190 const aggregateVersion = ( version ) => ( {
176191 mount : aggregateNumbers ( rows . map ( ( row ) => row [ version ] ?. mount ?. median ) ) ,
177192 unmount : aggregateNumbers ( rows . map ( ( row ) => row [ version ] ?. unmount ?. median ) ) ,
193+ update : aggregateNumbers ( rows . map ( ( row ) => row [ version ] ?. update ?. median ) ) ,
178194 mountMemory : aggregateNumbers ( rows . map ( ( row ) => row [ version ] ?. mountMemory ?. median ) ) ,
179195 unmountMemory : aggregateNumbers ( rows . map ( ( row ) => row [ version ] ?. unmountMemory ?. median ) ) ,
180- timeoutCount : rows . reduce (
181- ( total , row ) => total + ( row [ version ] ?. timeoutCount ?? 0 ) ,
182- 0 ,
183- ) ,
196+ timeoutCount : rows . reduce ( ( total , row ) => total + ( row [ version ] ?. timeoutCount ?? 0 ) , 0 ) ,
184197 } )
185198
186199 const v5 = aggregateVersion ( 'v5' )
@@ -203,6 +216,10 @@ async function main() {
203216 typeof v5 . unmountMemory . median === 'number' && typeof v6 . unmountMemory . median === 'number'
204217 ? v6 . unmountMemory . median - v5 . unmountMemory . median
205218 : null
219+ const updateDeltaMs =
220+ typeof v5 . update . median === 'number' && typeof v6 . update . median === 'number'
221+ ? v6 . update . median - v5 . update . median
222+ : null
206223
207224 return {
208225 count,
@@ -211,6 +228,7 @@ async function main() {
211228 v6,
212229 mountDeltaMs,
213230 unmountDeltaMs,
231+ updateDeltaMs,
214232 mountMemoryDeltaBytes,
215233 unmountMemoryDeltaBytes,
216234 mountDeltaSpreadPercent :
@@ -222,13 +240,19 @@ async function main() {
222240 ? Math . max ( v5 . unmount . spreadPercent , v6 . unmount . spreadPercent )
223241 : null ,
224242 mountMemoryDeltaSpreadPercent :
225- typeof v5 . mountMemory . spreadPercent === 'number' && typeof v6 . mountMemory . spreadPercent === 'number'
243+ typeof v5 . mountMemory . spreadPercent === 'number' &&
244+ typeof v6 . mountMemory . spreadPercent === 'number'
226245 ? Math . max ( v5 . mountMemory . spreadPercent , v6 . mountMemory . spreadPercent )
227246 : null ,
228247 unmountMemoryDeltaSpreadPercent :
229- typeof v5 . unmountMemory . spreadPercent === 'number' && typeof v6 . unmountMemory . spreadPercent === 'number'
248+ typeof v5 . unmountMemory . spreadPercent === 'number' &&
249+ typeof v6 . unmountMemory . spreadPercent === 'number'
230250 ? Math . max ( v5 . unmountMemory . spreadPercent , v6 . unmountMemory . spreadPercent )
231251 : null ,
252+ updateDeltaSpreadPercent :
253+ typeof v5 . update . spreadPercent === 'number' && typeof v6 . update . spreadPercent === 'number'
254+ ? Math . max ( v5 . update . spreadPercent , v6 . update . spreadPercent )
255+ : null ,
232256 }
233257 } )
234258
0 commit comments