33const CONSTANTS = require ( './constants' ) ;
44const { performance } = require ( 'perf_hooks' ) ;
55const Suite = require ( './suite' ) ;
6-
7- const PERCENTILES = [ 10 , 25 , 50 , 75 , 95 , 98 , 99 ] ;
8- function percentileIndex ( percentile , total ) {
9- return Math . max ( Math . floor ( ( total * percentile ) / 100 - 1 ) , 0 ) ;
10- }
6+ const fs = require ( 'fs' ) ;
7+ const child_process = require ( 'child_process' ) ;
8+ const stream = require ( 'stream/promises' ) ;
9+ const { Writable } = require ( 'stream' ) ;
1110
1211function timeSyncTask ( task , ctx ) {
1312 const start = performance . now ( ) ;
@@ -25,30 +24,54 @@ async function timeAsyncTask(task, ctx) {
2524 return ( end - start ) / 1000 ;
2625}
2726
27+ const awkFindMiddle = [
28+ // For each line, store the line in the array `a` with the current line number as the index
29+ '{ a[NR] = $0 }' ,
30+ // After processing all lines (END block), calculate the middle index based on the total line count (NR)
31+ 'END {' ,
32+ // Calculate `mid` as the integer division of the line count by 2
33+ ' mid = int(NR / 2);' ,
34+ // If the line count is odd, print the middle line (one-based index: mid + 1)
35+ ' if (NR % 2)' ,
36+ ' print a[mid + 1];' ,
37+ // If the line count is even, print the two middle lines
38+ ' else' ,
39+ ' print a[mid], a[mid + 1];' ,
40+ '}'
41+ ] . join ( ' ' ) ;
42+
2843/**
2944 * Returns the execution time for the benchmarks in mb/second
3045 *
3146 * This function internally calculates the 50th percentile execution time and uses
3247 * that as the median.
3348 *
3449 * @param {Benchmark } benchmark
35- * @param {{ rawData: number[], count: number} } data
3650 * @returns number
3751 */
38- function calculateMicroBench ( benchmark , data ) {
39- const rawData = data . rawData ;
40- const count = data . count ;
52+ async function calculateMicroBench ( benchmark ) {
53+ const pipeOptions = { stdio : [ 'pipe' , 'pipe' , 'inherit' ] } ;
54+ const sort = child_process . spawn ( 'sort' , [ '-n' ] , pipeOptions ) ;
55+ const awk = child_process . spawn ( 'awk' , [ awkFindMiddle ] , pipeOptions ) ;
56+
57+ let lines = '' ;
58+ const collect = new Writable ( {
59+ write : ( chunk , encoding , callback ) => {
60+ lines += encoding === 'buffer' ? chunk . toString ( 'utf8' ) : chunk ;
61+ callback ( ) ;
62+ }
63+ } ) ;
4164
42- const sortedData = [ ] . concat ( rawData ) . sort ( ) ;
65+ await stream . pipeline ( fs . createReadStream ( 'raw.dat' , 'utf8' ) , sort . stdin ) ;
66+ await stream . pipeline ( sort . stdout , awk . stdin ) ;
67+ await stream . pipeline ( awk . stdout , collect ) ;
4368
44- const percentiles = PERCENTILES . reduce ( ( acc , pct ) => {
45- acc [ pct ] = sortedData [ percentileIndex ( pct , count ) ] ;
46- return acc ;
47- } , { } ) ;
69+ fs . unlinkSync ( 'raw.dat' ) ;
4870
49- const medianExecution = percentiles [ 50 ] ;
71+ const [ value0 , value1 ] = lines . trim ( ) . split ( ' ' ) ;
72+ const median = value1 ? ( Number ( value0 ) + Number ( value1 ) ) / 2 : Number ( value0 ) ;
5073
51- return benchmark . taskSize / medianExecution ;
74+ return benchmark . taskSize / median ;
5275}
5376
5477class Runner {
@@ -126,6 +149,7 @@ class Runner {
126149 for ( const [ name , benchmark ] of benchmarks ) {
127150 this . reporter ( ` Executing Benchmark "${ name } "` ) ;
128151 result [ name ] = await this . _runBenchmark ( benchmark ) ;
152+ this . reporter ( ` Executed Benchmark "${ name } " =` , result [ name ] ) ;
129153 }
130154
131155 return result ;
@@ -142,11 +166,12 @@ class Runner {
142166 const ctx = { } ;
143167 try {
144168 await benchmark . setup . call ( ctx ) ;
145- const result = await this . _loopTask ( benchmark , ctx ) ;
169+ await this . _loopTask ( benchmark , ctx ) ;
146170 await benchmark . teardown . call ( ctx ) ;
147- return calculateMicroBench ( benchmark , result ) ;
171+ return await calculateMicroBench ( benchmark ) ;
148172 } catch ( error ) {
149- return this . _errorHandler ( error ) ;
173+ fs . unlinkSync ( 'raw.dat' ) ;
174+ this . _errorHandler ( error ) ;
150175 }
151176 }
152177
@@ -157,33 +182,38 @@ class Runner {
157182 * @returns {{ rawData: number[], count: number} }
158183 */
159184 async _loopTask ( benchmark , ctx ) {
160- const start = performance . now ( ) ;
161- const rawData = [ ] ;
162- const minExecutionCount = this . minExecutionCount ;
163- const minExecutionTime = this . minExecutionTime ;
164- const maxExecutionTime = this . maxExecutionTime ;
165- let time = performance . now ( ) - start ;
166- let count = 1 ;
167-
168- const taskTimer = benchmark . _taskType === 'sync' ? timeSyncTask : timeAsyncTask ;
169-
170- while ( time < maxExecutionTime && ( time < minExecutionTime || count < minExecutionCount ) ) {
171- await benchmark . beforeTask . call ( ctx ) ;
172- const executionTime = await taskTimer ( benchmark . task , ctx ) ;
173- rawData . push ( executionTime ) ;
174- count ++ ;
175- time = performance . now ( ) ;
185+ const rawDataFile = fs . openSync ( 'raw.dat' , 'w' ) ;
186+ try {
187+ const start = performance . now ( ) ;
188+ const minExecutionCount = this . minExecutionCount ;
189+ const minExecutionTime = this . minExecutionTime ;
190+ const maxExecutionTime = this . maxExecutionTime ;
191+ let time = performance . now ( ) - start ;
192+ let count = 1 ;
193+
194+ const taskTimer = benchmark . _taskType === 'sync' ? timeSyncTask : timeAsyncTask ;
195+
196+ while ( time < maxExecutionTime && ( time < minExecutionTime || count < minExecutionCount ) ) {
197+ await benchmark . beforeTask . call ( ctx ) ;
198+ const executionTime = await taskTimer ( benchmark . task , ctx ) ;
199+ fs . writeSync ( rawDataFile , String ( executionTime ) + '\n' ) ;
200+ count ++ ;
201+ time = performance . now ( ) ;
202+ }
203+ } finally {
204+ fs . closeSync ( rawDataFile ) ;
176205 }
177-
178- return {
179- rawData,
180- count
181- } ;
182206 }
183207
184- _errorHandler ( e ) {
185- console . error ( e ) ;
186- return NaN ;
208+ _errorHandler ( error ) {
209+ let currentError = error ;
210+ while ( currentError ) {
211+ this . reporter (
212+ `${ currentError !== error ? 'Caused by' : 'Error' } : ${ currentError . name } - ${ currentError . message } - ${ currentError . stack } `
213+ ) ;
214+ currentError = currentError . cause ;
215+ }
216+ throw error ;
187217 }
188218}
189219
0 commit comments