1414 */
1515
1616import pc from 'picocolors' ;
17- import { mean , stdDev } from '../stats.js' ;
1817import type {
1918 IFunctionStatistics ,
2019 IPairedComparison ,
2120 ISuiteReport ,
2221} from '../types.js' ;
2322
23+ function getRatio (
24+ fastest : IFunctionStatistics ,
25+ a : IFunctionStatistics ,
26+ b : IFunctionStatistics ,
27+ ) {
28+ if ( ! ( fastest . mean > 0 ) ) {
29+ return a . rawMean / b . rawMean ;
30+ }
31+
32+ return a . mean / b . mean ;
33+ }
34+
2435// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2536// Constants
2637// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -63,27 +74,27 @@ function lpad(s: string, w: number): string {
6374function ft ( ms : number ) : string {
6475 const a = Math . abs ( ms ) ;
6576 const sign = ms < 0 ? '−' : '' ;
66- if ( a === 0 ) return '0.00 ns' ;
77+ if ( a === 0 ) return '0.000 ns' ;
6778 if ( a < 0.000_000_000_001 ) return `${ sign } ${ ( a * 1e15 ) . toFixed ( 3 ) } as` ;
6879 if ( a < 0.000_000_001 ) return `${ sign } ${ ( a * 1e12 ) . toFixed ( 3 ) } fs` ;
6980 if ( a < 0.000_001 ) return `${ sign } ${ ( a * 1e9 ) . toFixed ( 3 ) } ps` ;
70- if ( a < 0.001 ) return `${ sign } ${ ( a * 1e6 ) . toFixed ( 2 ) } ns` ;
71- if ( a < 1 ) return `${ sign } ${ ( a * 1e3 ) . toFixed ( 2 ) } µs` ;
81+ if ( a < 0.001 ) return `${ sign } ${ ( a * 1e6 ) . toFixed ( 3 ) } ns` ;
82+ if ( a < 1 ) return `${ sign } ${ ( a * 1e3 ) . toFixed ( 3 ) } µs` ;
7283 if ( a < 1000 ) return `${ sign } ${ a . toFixed ( 3 ) } ms` ;
7384 return `${ sign } ${ ( a / 1000 ) . toFixed ( 3 ) } s` ;
7485}
7586
7687/** Format throughput as operations per second with SI suffix. */
7788function fops ( ms : number ) : string {
78- if ( ms <= 0 ) return '∞ op/s ' ;
89+ if ( ms <= 0 ) return '∞' ;
7990 const ops = 1000 / ms ;
80- if ( ops >= 1e18 ) return `${ ( ops / 1e18 ) . toFixed ( 2 ) } E op/s ` ;
81- if ( ops >= 1e15 ) return `${ ( ops / 1e15 ) . toFixed ( 2 ) } P op/s ` ;
82- if ( ops >= 1e12 ) return `${ ( ops / 1e12 ) . toFixed ( 2 ) } T op/s ` ;
83- if ( ops >= 1e9 ) return `${ ( ops / 1e9 ) . toFixed ( 2 ) } G op/s ` ;
84- if ( ops >= 1e6 ) return `${ ( ops / 1e6 ) . toFixed ( 2 ) } M op/s ` ;
85- if ( ops >= 1e3 ) return `${ ( ops / 1e3 ) . toFixed ( 2 ) } K op/s ` ;
86- return `${ ops . toFixed ( 2 ) } op/s ` ;
91+ if ( ops >= 1e18 ) return `${ ( ops / 1e18 ) . toFixed ( 2 ) } E` ;
92+ if ( ops >= 1e15 ) return `${ ( ops / 1e15 ) . toFixed ( 2 ) } P` ;
93+ if ( ops >= 1e12 ) return `${ ( ops / 1e12 ) . toFixed ( 2 ) } T` ;
94+ if ( ops >= 1e9 ) return `${ ( ops / 1e9 ) . toFixed ( 2 ) } G` ;
95+ if ( ops >= 1e6 ) return `${ ( ops / 1e6 ) . toFixed ( 2 ) } M` ;
96+ if ( ops >= 1e3 ) return `${ ( ops / 1e3 ) . toFixed ( 2 ) } k ` ;
97+ return `${ ops . toFixed ( 2 ) } ` ;
8798}
8899
89100/** Locale-formatted integer / number. */
@@ -290,6 +301,24 @@ function renderHeader(suite: Readonly<ISuiteReport>): string[] {
290301 L . push ( ' ' + bar ( '┃' ) + ' ' . repeat ( inner ) + bar ( '┃' ) ) ;
291302 L . push ( ' ' + bar ( '┗' + '━' . repeat ( inner ) + '┛' ) ) ;
292303
304+ if (
305+ suite . functions . some (
306+ ( fn ) => fn . name !== suite . baselineName && ! ( fn . mean > 0 ) ,
307+ )
308+ ) {
309+ L . push ( '' ) ;
310+ L . push (
311+ ' ' +
312+ pc . yellow ( '⚠' ) +
313+ ' ' +
314+ pc . yellow (
315+ `Raw ratios shown — some baseline-adjusted values are at or below the noise floor,` ,
316+ ) ,
317+ ) ;
318+ L . push ( ' ' + pc . yellow ( 'making adjusted ratios unreliable.' ) ) ;
319+ L . push ( '' ) ;
320+ }
321+
293322 return L ;
294323}
295324
@@ -328,12 +357,12 @@ function renderWinner(
328357 pc . dim ( `(${ fpv ( topComp . pValue ) } )` ) ,
329358 ) ;
330359 } else {
331- const ratio2 = second . mean / fastest . mean ;
360+ const ratio2 = getRatio ( fastest , second , fastest ) ;
332361 const parts = [ pc . dim ( `${ fmul ( ratio2 ) } faster than ${ second . name } ` ) ] ;
333362 if ( fns . length > 2 ) {
334363 parts . push (
335364 pc . dim (
336- `${ fmul ( slowest . mean / fastest . mean ) } vs ${ slowest . name } ` ,
365+ `${ fmul ( getRatio ( fastest , slowest , fastest ) ) } vs ${ slowest . name } ` ,
337366 ) ,
338367 ) ;
339368 }
@@ -389,11 +418,11 @@ function renderLeaderboard(
389418 ) ,
390419 ) ;
391420
392- const maxOps = fastest . mean > 0 ? 1 / fastest . mean : 0 ;
421+ const maxOps = fastest . mean > 0 ? 1 / fastest . mean : 1 / fastest . rawMean ;
393422
394423 for ( let i = 0 ; i < fns . length ; i ++ ) {
395424 const f = fns [ i ] ;
396- const ops = f . mean > 0 ? 1 / f . mean : 0 ;
425+ const ops = fastest . mean > 0 ? 1 / f . mean : 1 / f . rawMean ;
397426 const ratio = maxOps > 0 ? ops / maxOps : 0 ;
398427
399428 const medal = i < 3 ? MEDALS [ i ] : pc . dim ( `#${ i + 1 } ` ) ;
@@ -413,11 +442,9 @@ function renderLeaderboard(
413442 let rel : string ;
414443 if ( i === 0 ) {
415444 rel = pc . green ( ' fastest' ) ;
416- } else if ( fastest . mean > 0 ) {
417- const timesSlower = f . mean / fastest . mean ;
418- rel = pc . dim ( ` ${ fmul ( timesSlower ) } slower` ) ;
419445 } else {
420- rel = '' ;
446+ const timesSlower = getRatio ( fastest , f , fastest ) ;
447+ rel = pc . dim ( ` ${ fmul ( timesSlower ) } slower` ) ;
421448 }
422449
423450 L . push (
@@ -576,7 +603,9 @@ function renderComparisons(
576603
577604 const aFaster = fA . mean <= fB . mean ;
578605 const fasterName = aFaster ? c . a : c . b ;
579- const ratio = aFaster ? fB . mean / fA . mean : fA . mean / fB . mean ;
606+ const ratio = aFaster
607+ ? getRatio ( fns [ 0 ] , fB , fA )
608+ : getRatio ( fns [ 0 ] , fA , fB ) ;
580609
581610 L . push ( '' ) ;
582611 L . push ( ' ' + pc . bold ( c . a ) + pc . dim ( ' vs ' ) + pc . bold ( c . b ) ) ;
@@ -689,7 +718,7 @@ function renderMatrix(
689718 }
690719
691720 // ratio > 1 ⇒ row is faster
692- const ratio = colF . mean / rowF . mean ;
721+ const ratio = getRatio ( fns [ 0 ] , colF , rowF ) ;
693722
694723 const comp = comps . find (
695724 ( cc ) =>
@@ -726,8 +755,8 @@ function renderBaseline(
726755
727756 const L : string [ ] = [ ] ;
728757
729- const baseLineMean = mean ( baseline . rawSamples ) ;
730- const baselineStdDev = stdDev ( baseline . rawSamples ) ;
758+ const baseLineMean = baseline . rawMean ;
759+ const baselineStdDev = baseline . rawStdDev ;
731760
732761 L . push ( secLine ( 'Measurement Overhead' ) ) ;
733762 L . push ( '' ) ;
@@ -744,33 +773,31 @@ function renderBaseline(
744773 ' ' + pc . dim ( 'All reported times have this overhead subtracted.' ) ,
745774 ) ;
746775
747- if ( fastest . mean > 0 ) {
748- const ratio = baseLineMean / fastest . mean ;
749- if ( ratio > 0.1 ) {
750- L . push ( '' ) ;
751- L . push (
752- ' ' +
753- pc . yellow ( '⚠' ) +
754- ' ' +
755- pc . yellow (
756- `Overhead is ${ ( ratio * 100 ) . toFixed ( 1 ) } % of the fastest function.` ,
757- ) ,
758- ) ;
759- L . push (
760- ' ' +
761- pc . dim (
762- ' Consider increasing work per iteration for more accurate results.' ,
763- ) ,
764- ) ;
765- } else {
766- L . push (
767- ' ' +
768- pc . dim (
769- `Overhead is ${ ( ratio * 100 ) . toFixed ( 2 ) } % of the fastest — ` ,
770- ) +
771- pc . green ( 'negligible' ) ,
772- ) ;
773- }
776+ const ratio = baseLineMean / fastest . rawMean ;
777+ if ( ratio > 0.1 ) {
778+ L . push ( '' ) ;
779+ L . push (
780+ ' ' +
781+ pc . yellow ( '⚠' ) +
782+ ' ' +
783+ pc . yellow (
784+ `Overhead is ${ ( ratio * 100 ) . toFixed ( 1 ) } % of the fastest function.` ,
785+ ) ,
786+ ) ;
787+ L . push (
788+ ' ' +
789+ pc . dim (
790+ ' Consider increasing work per iteration for more accurate results.' ,
791+ ) ,
792+ ) ;
793+ } else {
794+ L . push (
795+ ' ' +
796+ pc . dim (
797+ `Overhead is ${ ( ratio * 100 ) . toFixed ( 2 ) } % of the fastest — ` ,
798+ ) +
799+ pc . green ( 'negligible' ) ,
800+ ) ;
774801 }
775802
776803 return L ;
0 commit comments