perf: add measurement coverage to benchmark
[poolifier.git] / benchmarks / benchmarks-utils.mjs
1 import crypto from 'node:crypto'
2 import fs from 'node:fs'
3 import {
4 DynamicClusterPool,
5 DynamicThreadPool,
6 FixedClusterPool,
7 FixedThreadPool,
8 PoolTypes,
9 WorkerTypes
10 } from '../lib/index.mjs'
11 import { WorkerFunctions } from './benchmarks-types.mjs'
12
13 export const runTest = async (pool, { taskExecutions, workerData }) => {
14 return new Promise((resolve, reject) => {
15 let executions = 0
16 for (let i = 1; i <= taskExecutions; i++) {
17 pool
18 .execute(workerData)
19 .then(() => {
20 ++executions
21 if (executions === taskExecutions) {
22 return resolve({ ok: 1 })
23 }
24 return null
25 })
26 .catch(err => {
27 console.error(err)
28 return reject(err)
29 })
30 }
31 })
32 }
33
34 export const generateRandomInteger = (
35 max = Number.MAX_SAFE_INTEGER,
36 min = 0
37 ) => {
38 if (max < min || max < 0 || min < 0) {
39 throw new RangeError('Invalid interval')
40 }
41 max = Math.floor(max)
42 if (min != null && min !== 0) {
43 min = Math.ceil(min)
44 return Math.floor(Math.random() * (max - min + 1)) + min
45 }
46 return Math.floor(Math.random() * (max + 1))
47 }
48
49 const jsonIntegerSerialization = n => {
50 for (let i = 0; i < n; i++) {
51 const o = {
52 a: i
53 }
54 JSON.stringify(o)
55 }
56 return { ok: 1 }
57 }
58
59 /**
60 * Intentionally inefficient implementation.
61 * @param {number} n - The number of fibonacci numbers to generate.
62 * @returns {number} - The nth fibonacci number.
63 */
64 const fibonacci = n => {
65 if (n <= 1) return n
66 return fibonacci(n - 1) + fibonacci(n - 2)
67 }
68
69 /**
70 * Intentionally inefficient implementation.
71 * @param {number} n - The number to calculate the factorial of.
72 * @returns {number} - The factorial of n.
73 */
74 const factorial = n => {
75 if (n === 0) {
76 return 1
77 }
78 return factorial(n - 1) * n
79 }
80
81 const readWriteFiles = (
82 n,
83 baseDirectory = `/tmp/poolifier-benchmarks/${crypto.randomInt(
84 281474976710655
85 )}`
86 ) => {
87 if (fs.existsSync(baseDirectory) === true) {
88 fs.rmSync(baseDirectory, { recursive: true })
89 }
90 fs.mkdirSync(baseDirectory, { recursive: true })
91 for (let i = 0; i < n; i++) {
92 const filePath = `${baseDirectory}/${i}`
93 fs.writeFileSync(filePath, i.toString(), {
94 encoding: 'utf8',
95 flag: 'a'
96 })
97 fs.readFileSync(filePath, 'utf8')
98 }
99 fs.rmSync(baseDirectory, { recursive: true })
100 return { ok: 1 }
101 }
102
103 export const executeWorkerFunction = data => {
104 switch (data.function) {
105 case WorkerFunctions.jsonIntegerSerialization:
106 return jsonIntegerSerialization(data.taskSize || 1000)
107 case WorkerFunctions.fibonacci:
108 return fibonacci(data.taskSize || 1000)
109 case WorkerFunctions.factorial:
110 return factorial(data.taskSize || 1000)
111 case WorkerFunctions.readWriteFiles:
112 return readWriteFiles(data.taskSize || 1000)
113 default:
114 throw new Error('Unknown worker function')
115 }
116 }
117
118 export const buildPool = (workerType, poolType, poolSize, poolOptions) => {
119 switch (poolType) {
120 case PoolTypes.fixed:
121 switch (workerType) {
122 case WorkerTypes.thread:
123 return new FixedThreadPool(
124 poolSize,
125 './benchmarks/internal/thread-worker.mjs',
126 poolOptions
127 )
128 case WorkerTypes.cluster:
129 return new FixedClusterPool(
130 poolSize,
131 './benchmarks/internal/cluster-worker.mjs',
132 poolOptions
133 )
134 }
135 break
136 case PoolTypes.dynamic:
137 switch (workerType) {
138 case WorkerTypes.thread:
139 return new DynamicThreadPool(
140 Math.floor(poolSize / 2),
141 poolSize,
142 './benchmarks/internal/thread-worker.mjs',
143 poolOptions
144 )
145 case WorkerTypes.cluster:
146 return new DynamicClusterPool(
147 Math.floor(poolSize / 2),
148 poolSize,
149 './benchmarks/internal/cluster-worker.mjs',
150 poolOptions
151 )
152 }
153 break
154 }
155 }