1
1
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2
2
/* eslint-disable @typescript-eslint/no-var-requires */
3
- import { performance } from 'perf_hooks' ;
3
+ import { createHistogram } from 'perf_hooks' ;
4
4
import { readFile } from 'fs/promises' ;
5
5
import { cpus , totalmem } from 'os' ;
6
6
import { 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' ;
8
11
const exec = promisify ( execCb ) ;
9
12
10
13
const hw = cpus ( ) ;
@@ -23,27 +26,26 @@ export const systemInfo = iterations =>
23
26
export const readJSONFile = async path =>
24
27
JSON . parse ( await readFile ( new URL ( path , import . meta. url ) , { encoding : 'utf8' } ) ) ;
25
28
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 ) {
34
30
let thrownError = null ;
31
+ const histogram = createHistogram ( ) ;
35
32
for ( let i = 0 ; i < iterations ; i ++ ) {
36
- const start = performance . now ( ) ;
37
33
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
+ }
39
43
} catch ( error ) {
40
44
thrownError = error ;
41
45
break ;
42
46
}
43
- const end = performance . now ( ) ;
44
- measurements . push ( end - start ) ;
45
47
}
46
- return { result : average ( measurements ) . toFixed ( 8 ) , thrownError } ;
48
+ return { histogram , thrownError } ;
47
49
}
48
50
49
51
export function getCurrentLocalBSON ( libs ) {
@@ -52,15 +54,15 @@ export function getCurrentLocalBSON(libs) {
52
54
53
55
export async function getLibs ( ) {
54
56
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
+ // })(),
64
66
( async ( ) => ( {
65
67
name : 'released' ,
66
68
lib : await import ( '../../node_modules/bson_latest/lib/bson.js' ) ,
@@ -73,12 +75,12 @@ export async function getLibs() {
73
75
lib : { ...legacyBSON , ...legacyBSON . prototype } ,
74
76
version : ( await readJSONFile ( '../../node_modules/bson_legacy/package.json' ) ) . version
75
77
} ;
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
+ // }))()
82
84
] ) . catch ( error => {
83
85
console . error ( error ) ;
84
86
console . error (
@@ -91,6 +93,27 @@ export async function getLibs() {
91
93
} ) ;
92
94
}
93
95
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
+
94
117
/**
95
118
* ```ts
96
119
* interface {
@@ -109,19 +132,23 @@ export async function runner({ iterations, setup, name, run, skip }) {
109
132
const BSONLibs = await getLibs ( ) ;
110
133
const setupResult = setup ?. ( BSONLibs ) ?? null ;
111
134
112
- console . log ( `\ntesting: ${ name } ` ) ;
135
+ console . log ( '-' . repeat ( 155 ) ) ;
113
136
114
137
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 ) ;
116
141
if ( thrownError != null ) {
117
142
console . log (
118
143
`${ bson . name . padEnd ( 14 , ' ' ) } - v ${ bson . version . padEnd ( 8 , ' ' ) } - error ${ thrownError } `
119
144
) ;
120
145
} 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 ) ;
124
147
}
148
+ const profile = v8Profiler . stopProfiling ( profileName ) ;
149
+ const result = await promisify ( profile . export . bind ( profile ) ) ( ) ;
150
+ await writeFile ( `${ profileName } .cpuprofile` , result ) ;
151
+ profile . delete ( ) ;
125
152
}
126
153
127
154
console . log ( ) ;
0 commit comments