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