build: revert commit 211b4eb
[poolifier.git] / benchmarks / benchmarks-utils.js
CommitLineData
ab7bb4f8
JB
1const crypto = require('node:crypto')
2const assert = require('node:assert')
3const fs = require('node:fs')
4const Benchmark = require('benchmark')
5const {
cdace0e5
JB
6 DynamicClusterPool,
7 DynamicThreadPool,
8 FixedClusterPool,
d9d8c14e 9 FixedThreadPool,
617848e3 10 Measurements,
d9d8c14e 11 PoolTypes,
cde5b54e 12 WorkerChoiceStrategies,
d9d8c14e 13 WorkerTypes
ab7bb4f8
JB
14} = require('../lib/index.js')
15const { TaskFunctions } = require('./benchmarks-types.js')
2d2e32c2 16
ab7bb4f8 17const buildPoolifierPool = (workerType, poolType, poolSize, poolOptions) => {
479ba9f6
JB
18 switch (poolType) {
19 case PoolTypes.fixed:
20 switch (workerType) {
21 case WorkerTypes.thread:
22 return new FixedThreadPool(
23 poolSize,
24 './benchmarks/internal/thread-worker.mjs',
25 poolOptions
26 )
27 case WorkerTypes.cluster:
28 return new FixedClusterPool(
29 poolSize,
ab7bb4f8 30 './benchmarks/internal/cluster-worker.js',
479ba9f6
JB
31 poolOptions
32 )
33 }
34 break
35 case PoolTypes.dynamic:
36 switch (workerType) {
37 case WorkerTypes.thread:
38 return new DynamicThreadPool(
39 Math.floor(poolSize / 2),
40 poolSize,
41 './benchmarks/internal/thread-worker.mjs',
42 poolOptions
43 )
44 case WorkerTypes.cluster:
45 return new DynamicClusterPool(
46 Math.floor(poolSize / 2),
47 poolSize,
ab7bb4f8 48 './benchmarks/internal/cluster-worker.js',
479ba9f6
JB
49 poolOptions
50 )
51 }
52 break
53 }
54}
55
ab7bb4f8 56const runPoolifierPool = async (pool, { taskExecutions, workerData }) => {
cde5b54e 57 return await new Promise((resolve, reject) => {
ff5e76e1 58 let executions = 0
cdace0e5 59 for (let i = 1; i <= taskExecutions; i++) {
ff5e76e1
JB
60 pool
61 .execute(workerData)
fe2f6f84 62 .then(() => {
0762fbb1 63 ++executions
cdace0e5 64 if (executions === taskExecutions) {
cde5b54e 65 resolve({ ok: 1 })
ff5e76e1 66 }
fefd3cef 67 return undefined
ff5e76e1 68 })
041dc05b 69 .catch(err => {
23ff945a 70 console.error(err)
cde5b54e 71 reject(err)
23ff945a 72 })
ff5e76e1
JB
73 }
74 })
75}
76
ab7bb4f8 77const runPoolifierPoolBenchmark = async (
cde5b54e 78 name,
49d60f11
JB
79 workerType,
80 poolType,
81 poolSize,
cde5b54e
JB
82 { taskExecutions, workerData }
83) => {
84 return await new Promise((resolve, reject) => {
9f99eb9b
JB
85 const pool = buildPoolifierPool(workerType, poolType, poolSize)
86 const suite = new Benchmark.Suite(name)
cde5b54e 87 try {
cde5b54e
JB
88 for (const workerChoiceStrategy of Object.values(
89 WorkerChoiceStrategies
90 )) {
91 for (const enableTasksQueue of [false, true]) {
617848e3
JB
92 if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
93 for (const measurement of [
94 Measurements.runTime,
95 Measurements.elu
96 ]) {
97 suite.add(
405896a3 98 `${name} with ${workerChoiceStrategy}, with ${measurement} and ${
617848e3
JB
99 enableTasksQueue ? 'with' : 'without'
100 } tasks queue`,
101 async () => {
102 pool.setWorkerChoiceStrategy(workerChoiceStrategy, {
103 measurement
104 })
105 pool.enableTasksQueue(enableTasksQueue)
106 assert.strictEqual(
107 pool.opts.workerChoiceStrategy,
108 workerChoiceStrategy
109 )
110 assert.strictEqual(
111 pool.opts.enableTasksQueue,
112 enableTasksQueue
113 )
114 assert.strictEqual(
115 pool.opts.workerChoiceStrategyOptions.measurement,
116 measurement
117 )
118 await runPoolifierPool(pool, {
119 taskExecutions,
120 workerData
121 })
122 }
cde5b54e 123 )
cde5b54e 124 }
617848e3
JB
125 } else {
126 suite.add(
405896a3 127 `${name} with ${workerChoiceStrategy} and ${
617848e3
JB
128 enableTasksQueue ? 'with' : 'without'
129 } tasks queue`,
130 async () => {
131 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
132 pool.enableTasksQueue(enableTasksQueue)
133 assert.strictEqual(
134 pool.opts.workerChoiceStrategy,
135 workerChoiceStrategy
136 )
137 assert.strictEqual(pool.opts.enableTasksQueue, enableTasksQueue)
138 await runPoolifierPool(pool, {
139 taskExecutions,
140 workerData
141 })
142 }
143 )
144 }
cde5b54e
JB
145 }
146 }
147 suite
148 .on('cycle', event => {
149 console.info(event.target.toString())
150 })
151 .on('complete', async function () {
152 console.info(
153 'Fastest is ' +
154 LIST_FORMATTER.format(this.filter('fastest').map('name'))
155 )
156 await pool.destroy()
157 resolve()
158 })
159 .run({ async: true })
160 } catch (error) {
49d60f11
JB
161 pool
162 .destroy()
163 .then(() => {
164 return reject(error)
165 })
166 .catch(() => {})
cde5b54e
JB
167 }
168 })
a7825346
JB
169}
170
ab7bb4f8 171const LIST_FORMATTER = new Intl.ListFormat('en-US', {
f1c674cd
JB
172 style: 'long',
173 type: 'conjunction'
174})
175
ab7bb4f8 176const generateRandomInteger = (max = Number.MAX_SAFE_INTEGER, min = 0) => {
548140e6 177 if (max < min || max < 0 || min < 0) {
4af5c11a
JB
178 throw new RangeError('Invalid interval')
179 }
c2d7d79b 180 max = Math.floor(max)
4af5c11a 181 if (min != null && min !== 0) {
c2d7d79b 182 min = Math.ceil(min)
872585ea 183 return Math.floor(Math.random() * (max - min + 1)) + min
74750c7f 184 }
872585ea 185 return Math.floor(Math.random() * (max + 1))
74750c7f
JB
186}
187
041dc05b 188const jsonIntegerSerialization = n => {
cdace0e5
JB
189 for (let i = 0; i < n; i++) {
190 const o = {
191 a: i
192 }
193 JSON.stringify(o)
194 }
30b963d4 195 return { ok: 1 }
cdace0e5
JB
196}
197
bdacc2d2
JB
198/**
199 * Intentionally inefficient implementation.
7d82d90e
JB
200 * @param {number} n - The number of fibonacci numbers to generate.
201 * @returns {number} - The nth fibonacci number.
bdacc2d2 202 */
041dc05b 203const fibonacci = n => {
024daf59 204 if (n <= 1) return n
bdacc2d2
JB
205 return fibonacci(n - 1) + fibonacci(n - 2)
206}
207
7d82d90e
JB
208/**
209 * Intentionally inefficient implementation.
7d82d90e
JB
210 * @param {number} n - The number to calculate the factorial of.
211 * @returns {number} - The factorial of n.
212 */
041dc05b 213const factorial = n => {
7d82d90e
JB
214 if (n === 0) {
215 return 1
7d82d90e 216 }
965415bb 217 return factorial(n - 1) * n
7d82d90e
JB
218}
219
bac873bd 220const readWriteFiles = (
670734fc
JB
221 n,
222 baseDirectory = `/tmp/poolifier-benchmarks/${crypto.randomInt(
223 281474976710655
224 )}`
bac873bd 225) => {
670734fc
JB
226 if (fs.existsSync(baseDirectory) === true) {
227 fs.rmSync(baseDirectory, { recursive: true })
cdace0e5 228 }
670734fc 229 fs.mkdirSync(baseDirectory, { recursive: true })
cdace0e5
JB
230 for (let i = 0; i < n; i++) {
231 const filePath = `${baseDirectory}/${i}`
232 fs.writeFileSync(filePath, i.toString(), {
233 encoding: 'utf8',
234 flag: 'a'
235 })
236 fs.readFileSync(filePath, 'utf8')
237 }
670734fc 238 fs.rmSync(baseDirectory, { recursive: true })
30b963d4 239 return { ok: 1 }
cdace0e5
JB
240}
241
ab7bb4f8 242const executeTaskFunction = data => {
2d2e32c2 243 switch (data.function) {
dbca3be9 244 case TaskFunctions.jsonIntegerSerialization:
d1a9aa41 245 return jsonIntegerSerialization(data.taskSize || 1000)
dbca3be9 246 case TaskFunctions.fibonacci:
d1a9aa41 247 return fibonacci(data.taskSize || 1000)
dbca3be9 248 case TaskFunctions.factorial:
d1a9aa41 249 return factorial(data.taskSize || 1000)
dbca3be9 250 case TaskFunctions.readWriteFiles:
cdace0e5 251 return readWriteFiles(data.taskSize || 1000)
2d2e32c2 252 default:
dbca3be9 253 throw new Error('Unknown task function')
2d2e32c2
JB
254 }
255}
ab7bb4f8
JB
256
257module.exports = {
258 LIST_FORMATTER,
ab7bb4f8
JB
259 executeTaskFunction,
260 generateRandomInteger,
261 runPoolifierPoolBenchmark
262}