test: switch thread worker to ESM
[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(
405896a3 103 `${name} with ${workerChoiceStrategy}, with ${measurement} and ${
617848e3
JB
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(
405896a3 132 `${name} with ${workerChoiceStrategy} and ${
617848e3
JB
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
bac873bd
JB
176export const generateRandomInteger = (
177 max = Number.MAX_SAFE_INTEGER,
178 min = 0
179) => {
548140e6 180 if (max < min || max < 0 || min < 0) {
4af5c11a
JB
181 throw new RangeError('Invalid interval')
182 }
c2d7d79b 183 max = Math.floor(max)
4af5c11a 184 if (min != null && min !== 0) {
c2d7d79b 185 min = Math.ceil(min)
872585ea 186 return Math.floor(Math.random() * (max - min + 1)) + min
74750c7f 187 }
872585ea 188 return Math.floor(Math.random() * (max + 1))
74750c7f
JB
189}
190
041dc05b 191const jsonIntegerSerialization = n => {
cdace0e5
JB
192 for (let i = 0; i < n; i++) {
193 const o = {
194 a: i
195 }
196 JSON.stringify(o)
197 }
30b963d4 198 return { ok: 1 }
cdace0e5
JB
199}
200
bdacc2d2
JB
201/**
202 * Intentionally inefficient implementation.
7d82d90e
JB
203 * @param {number} n - The number of fibonacci numbers to generate.
204 * @returns {number} - The nth fibonacci number.
bdacc2d2 205 */
041dc05b 206const fibonacci = n => {
024daf59 207 if (n <= 1) return n
bdacc2d2
JB
208 return fibonacci(n - 1) + fibonacci(n - 2)
209}
210
7d82d90e
JB
211/**
212 * Intentionally inefficient implementation.
7d82d90e
JB
213 * @param {number} n - The number to calculate the factorial of.
214 * @returns {number} - The factorial of n.
215 */
041dc05b 216const factorial = n => {
7d82d90e
JB
217 if (n === 0) {
218 return 1
7d82d90e 219 }
965415bb 220 return factorial(n - 1) * n
7d82d90e
JB
221}
222
bac873bd 223const readWriteFiles = (
670734fc
JB
224 n,
225 baseDirectory = `/tmp/poolifier-benchmarks/${crypto.randomInt(
226 281474976710655
227 )}`
bac873bd 228) => {
670734fc
JB
229 if (fs.existsSync(baseDirectory) === true) {
230 fs.rmSync(baseDirectory, { recursive: true })
cdace0e5 231 }
670734fc 232 fs.mkdirSync(baseDirectory, { recursive: true })
cdace0e5
JB
233 for (let i = 0; i < n; i++) {
234 const filePath = `${baseDirectory}/${i}`
235 fs.writeFileSync(filePath, i.toString(), {
236 encoding: 'utf8',
237 flag: 'a'
238 })
239 fs.readFileSync(filePath, 'utf8')
240 }
670734fc 241 fs.rmSync(baseDirectory, { recursive: true })
30b963d4 242 return { ok: 1 }
cdace0e5
JB
243}
244
041dc05b 245export const executeTaskFunction = data => {
2d2e32c2 246 switch (data.function) {
dbca3be9 247 case TaskFunctions.jsonIntegerSerialization:
d1a9aa41 248 return jsonIntegerSerialization(data.taskSize || 1000)
dbca3be9 249 case TaskFunctions.fibonacci:
d1a9aa41 250 return fibonacci(data.taskSize || 1000)
dbca3be9 251 case TaskFunctions.factorial:
d1a9aa41 252 return factorial(data.taskSize || 1000)
dbca3be9 253 case TaskFunctions.readWriteFiles:
cdace0e5 254 return readWriteFiles(data.taskSize || 1000)
2d2e32c2 255 default:
dbca3be9 256 throw new Error('Unknown task function')
2d2e32c2
JB
257 }
258}