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