11/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
22/* eslint-disable @typescript-eslint/no-var-requires */
3- import { performance } from 'perf_hooks' ;
3+ import { createHistogram } from 'perf_hooks' ;
44import { readFile } from 'fs/promises' ;
55import { cpus , totalmem } from 'os' ;
66import { exec as execCb } from 'child_process' ;
7- import { promisify } from 'util' ;
7+ import { promisify , types } from 'util' ;
8+ import { writeFile } from 'fs/promises' ;
9+ import v8Profiler from 'v8-profiler-next' ;
10+ import chalk from 'chalk' ;
811const exec = promisify ( execCb ) ;
912
1013const hw = cpus ( ) ;
@@ -23,27 +26,26 @@ export const systemInfo = iterations =>
2326export const readJSONFile = async path =>
2427 JSON . parse ( await readFile ( new URL ( path , import . meta. url ) , { encoding : 'utf8' } ) ) ;
2528
26- function average ( array ) {
27- let sum = 0 ;
28- for ( const value of array ) sum += value ;
29- return sum / array . length ;
30- }
31-
32- function testPerformance ( lib , [ fn , arg ] , iterations ) {
33- let measurements = [ ] ;
29+ async function testPerformance ( lib , [ fn , arg ] , iterations ) {
3430 let thrownError = null ;
31+ const histogram = createHistogram ( ) ;
3532 for ( let i = 0 ; i < iterations ; i ++ ) {
36- const start = performance . now ( ) ;
3733 try {
38- fn ( i , lib , arg ) ;
34+ if ( types . isAsyncFunction ( fn ) ) {
35+ histogram . recordDelta ( ) ;
36+ await fn ( i , lib , arg ) ;
37+ histogram . recordDelta ( ) ;
38+ } else {
39+ histogram . recordDelta ( ) ;
40+ fn ( i , lib , arg ) ;
41+ histogram . recordDelta ( ) ;
42+ }
3943 } catch ( error ) {
4044 thrownError = error ;
4145 break ;
4246 }
43- const end = performance . now ( ) ;
44- measurements . push ( end - start ) ;
4547 }
46- return { result : average ( measurements ) . toFixed ( 8 ) , thrownError } ;
48+ return { histogram , thrownError } ;
4749}
4850
4951export function getCurrentLocalBSON ( libs ) {
@@ -52,15 +54,15 @@ export function getCurrentLocalBSON(libs) {
5254
5355export async function getLibs ( ) {
5456 return await Promise . all ( [
55- ( async ( ) => {
56- const { stdout } = await exec ( 'git rev-parse --short HEAD' ) ;
57- const hash = stdout . trim ( ) ;
58- return {
59- name : 'local' ,
60- lib : await import ( '../../lib/bson.js' ) ,
61- version : hash
62- } ;
63- } ) ( ) ,
57+ // (async () => {
58+ // const { stdout } = await exec('git rev-parse --short HEAD');
59+ // const hash = stdout.trim();
60+ // return {
61+ // name: 'local',
62+ // lib: await import('../../lib/bson.js'),
63+ // version: hash
64+ // };
65+ // })(),
6466 ( async ( ) => ( {
6567 name : 'released' ,
6668 lib : await import ( '../../node_modules/bson_latest/lib/bson.js' ) ,
@@ -73,12 +75,12 @@ export async function getLibs() {
7375 lib : { ...legacyBSON , ...legacyBSON . prototype } ,
7476 version : ( await readJSONFile ( '../../node_modules/bson_legacy/package.json' ) ) . version
7577 } ;
76- } ) ( ) ,
77- ( async ( ) => ( {
78- name : 'bson-ext' ,
79- lib : await import ( '../../node_modules/bson_ext/lib/index.js' ) ,
80- version : ( await readJSONFile ( '../../node_modules/bson_ext/package.json' ) ) . version
81- } ) ) ( )
78+ } ) ( )
79+ // (async () => ({
80+ // name: 'bson-ext',
81+ // lib: await import('../../node_modules/bson_ext/lib/index.js'),
82+ // version: (await readJSONFile('../../node_modules/bson_ext/package.json')).version
83+ // }))()
8284 ] ) . catch ( error => {
8385 console . error ( error ) ;
8486 console . error (
@@ -91,6 +93,27 @@ export async function getLibs() {
9193 } ) ;
9294}
9395
96+ const printHistogram = ( name , h ) => {
97+ const makeReadableTime = nanoseconds => ( nanoseconds / 1e6 ) . toFixed ( 3 ) . padStart ( 7 , ' ' ) ;
98+ console . log ( ) ;
99+ console . log ( chalk . green ( name ) ) ;
100+ console . log ( '-' . repeat ( 155 ) ) ;
101+ process . stdout . write ( `| ${ chalk . cyan ( 'max' ) } : ${ chalk . red ( makeReadableTime ( h . max ) ) } ms |` ) ;
102+ process . stdout . write ( ` ${ chalk . cyan ( 'min' ) } : ${ chalk . red ( makeReadableTime ( h . min ) ) } ms |` ) ;
103+ process . stdout . write ( ` ${ chalk . cyan ( 'mean' ) } : ${ chalk . red ( makeReadableTime ( h . mean ) ) } ms |` ) ;
104+ process . stdout . write ( ` ${ chalk . cyan ( 'stddev' ) } : ${ chalk . red ( makeReadableTime ( h . stddev ) ) } ms |` ) ;
105+ process . stdout . write (
106+ ` ${ chalk . cyan ( 'p90th' ) } : ${ chalk . red ( makeReadableTime ( h . percentile ( 90 ) ) ) } ms |`
107+ ) ;
108+ process . stdout . write (
109+ ` ${ chalk . cyan ( 'p95th' ) } : ${ chalk . red ( makeReadableTime ( h . percentile ( 95 ) ) ) } ms |`
110+ ) ;
111+ process . stdout . write (
112+ ` ${ chalk . cyan ( 'p99th' ) } : ${ chalk . red ( makeReadableTime ( h . percentile ( 99 ) ) ) } ms |`
113+ ) ;
114+ console . log ( '\n' + '-' . repeat ( 155 ) ) ;
115+ } ;
116+
94117/**
95118 * ```ts
96119 * interface {
@@ -109,19 +132,23 @@ export async function runner({ iterations, setup, name, run, skip }) {
109132 const BSONLibs = await getLibs ( ) ;
110133 const setupResult = setup ?. ( BSONLibs ) ?? null ;
111134
112- console . log ( `\ntesting: ${ name } ` ) ;
135+ console . log ( '-' . repeat ( 155 ) ) ;
113136
114137 for ( const bson of BSONLibs ) {
115- const { result : perf , thrownError } = testPerformance ( bson , [ run , setupResult ] , iterations ) ;
138+ const profileName = `${ bson . name } _${ name } ` ;
139+ v8Profiler . startProfiling ( profileName , true ) ;
140+ const { histogram, thrownError } = await testPerformance ( bson , [ run , setupResult ] , iterations ) ;
116141 if ( thrownError != null ) {
117142 console . log (
118143 `${ bson . name . padEnd ( 14 , ' ' ) } - v ${ bson . version . padEnd ( 8 , ' ' ) } - error ${ thrownError } `
119144 ) ;
120145 } else {
121- console . log (
122- `${ bson . name . padEnd ( 14 , ' ' ) } - v ${ bson . version . padEnd ( 8 , ' ' ) } - avg ${ perf } ms`
123- ) ;
146+ printHistogram ( `${ chalk . greenBright ( bson . name ) } - ${ chalk . blue ( name ) } ` , histogram ) ;
124147 }
148+ const profile = v8Profiler . stopProfiling ( profileName ) ;
149+ const result = await promisify ( profile . export . bind ( profile ) ) ( ) ;
150+ await writeFile ( `${ profileName } .cpuprofile` , result ) ;
151+ profile . delete ( ) ;
125152 }
126153
127154 console . log ( ) ;
0 commit comments