perf: use tatami-ng for continous benchmarking
[poolifier.git] / benchmarks / benchmarks-utils.mjs
CommitLineData
0804b9b4 1import { strictEqual } from 'node:assert'
ce711c4a 2import { env } from 'node:process'
0804b9b4
JB
3
4import Benchmark from 'benchmark'
98f60ddd 5import { bench, clear, group, run } from 'tatami-ng'
0804b9b4
JB
6
7import {
8 DynamicClusterPool,
9 DynamicThreadPool,
10 FixedClusterPool,
11 FixedThreadPool,
12 Measurements,
13 PoolTypes,
14 WorkerChoiceStrategies,
15 WorkerTypes
16} from '../lib/index.mjs'
a6bef8d2 17import { executeTaskFunction } from './benchmarks-utils.cjs'
0804b9b4
JB
18
19const buildPoolifierPool = (workerType, poolType, poolSize, poolOptions) => {
20 switch (poolType) {
21 case PoolTypes.fixed:
22 switch (workerType) {
23 case WorkerTypes.thread:
24 return new FixedThreadPool(
25 poolSize,
26 './benchmarks/internal/thread-worker.mjs',
27 poolOptions
28 )
29 case WorkerTypes.cluster:
30 return new FixedClusterPool(
31 poolSize,
32 './benchmarks/internal/cluster-worker.cjs',
33 poolOptions
34 )
35 }
36 break
37 case PoolTypes.dynamic:
38 switch (workerType) {
39 case WorkerTypes.thread:
40 return new DynamicThreadPool(
41 Math.floor(poolSize / 2),
42 poolSize,
43 './benchmarks/internal/thread-worker.mjs',
44 poolOptions
45 )
46 case WorkerTypes.cluster:
47 return new DynamicClusterPool(
48 Math.floor(poolSize / 2),
49 poolSize,
50 './benchmarks/internal/cluster-worker.cjs',
51 poolOptions
52 )
53 }
54 break
55 }
56}
57
58const runPoolifierPool = async (pool, { taskExecutions, workerData }) => {
59 return await new Promise((resolve, reject) => {
60 let executions = 0
61 for (let i = 1; i <= taskExecutions; i++) {
62 pool
63 .execute(workerData)
64 .then(() => {
65 ++executions
66 if (executions === taskExecutions) {
67 resolve({ ok: 1 })
68 }
69 return undefined
70 })
71 .catch(err => {
72 console.error(err)
73 reject(err)
74 })
75 }
76 })
77}
78
79export const runPoolifierBenchmarkBenchmarkJs = async (
6da2cd97
JB
80 name,
81 workerType,
82 poolType,
83 poolSize,
84 poolOptions,
85 { taskExecutions, workerData }
86) => {
87 return await new Promise((resolve, reject) => {
88 const pool = buildPoolifierPool(workerType, poolType, poolSize, poolOptions)
89 let workerChoiceStrategy
90 let enableTasksQueue
91 let workerChoiceStrategyOptions
92 if (poolOptions != null) {
93 ({
94 workerChoiceStrategy,
95 enableTasksQueue,
96 workerChoiceStrategyOptions
97 } = poolOptions)
98 }
99 const measurement = workerChoiceStrategyOptions?.measurement
100 new Benchmark(
101 `${name} with ${workerChoiceStrategy ?? pool.opts.workerChoiceStrategy}${
102 measurement != null ? `, with ${measurement}` : ''
103 } and ${enableTasksQueue ? 'with' : 'without'} tasks queue`,
104 async () => {
105 await runPoolifierPool(pool, {
106 taskExecutions,
107 workerData
108 })
109 },
110 {
111 onStart: () => {
112 if (workerChoiceStrategy != null) {
113 strictEqual(pool.opts.workerChoiceStrategy, workerChoiceStrategy)
114 }
115 if (enableTasksQueue != null) {
116 strictEqual(pool.opts.enableTasksQueue, enableTasksQueue)
117 }
118 if (measurement != null) {
119 strictEqual(
120 pool.opts.workerChoiceStrategyOptions.measurement,
121 measurement
122 )
123 }
124 },
125 onComplete: event => {
126 console.info(event.target.toString())
127 if (pool.started && !pool.destroying) {
128 pool.destroy().then(resolve).catch(reject)
129 } else {
130 resolve()
131 }
132 },
133 onError: event => {
134 if (pool.started && !pool.destroying) {
135 pool
136 .destroy()
137 .then(() => {
138 return reject(event.target.error)
139 })
140 .catch(() => {})
141 } else {
142 reject(event.target.error)
143 }
144 }
145 }
cc8689da 146 ).run({ async: true })
6da2cd97
JB
147 })
148}
149
150export const runPoolifierBenchmarkBenchmarkJsSuite = async (
0804b9b4
JB
151 name,
152 workerType,
153 poolType,
154 poolSize,
155 { taskExecutions, workerData }
156) => {
157 return await new Promise((resolve, reject) => {
6aec0be3 158 const pool = buildPoolifierPool(workerType, poolType, poolSize)
47ab5d23
JB
159 const suite = new Benchmark.Suite(name, {
160 onComplete: () => {
161 if (pool.started && !pool.destroying) {
162 pool.destroy().then(resolve).catch(reject)
163 } else {
164 resolve()
fe7488e9 165 }
47ab5d23
JB
166 },
167 onCycle: event => {
168 console.info(event.target.toString())
169 },
170 onError: event => {
171 if (pool.started && !pool.destroying) {
172 pool
173 .destroy()
174 .then(() => {
175 return reject(event.target.error)
176 })
177 .catch(() => {})
178 } else {
179 reject(event.target.error)
180 }
181 }
182 })
183 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
184 for (const enableTasksQueue of [false, true]) {
185 if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
186 for (const measurement of [Measurements.runTime, Measurements.elu]) {
0804b9b4 187 suite.add(
6da2cd97 188 `${name} with ${workerChoiceStrategy}, with ${measurement} and ${
0804b9b4
JB
189 enableTasksQueue ? 'with' : 'without'
190 } tasks queue`,
191 async () => {
0804b9b4
JB
192 await runPoolifierPool(pool, {
193 taskExecutions,
194 workerData
195 })
083213c6
JB
196 },
197 {
198 onStart: () => {
47ab5d23
JB
199 pool.setWorkerChoiceStrategy(workerChoiceStrategy, {
200 measurement
201 })
083213c6
JB
202 pool.enableTasksQueue(enableTasksQueue)
203 strictEqual(
204 pool.opts.workerChoiceStrategy,
205 workerChoiceStrategy
206 )
207 strictEqual(pool.opts.enableTasksQueue, enableTasksQueue)
47ab5d23
JB
208 strictEqual(
209 pool.opts.workerChoiceStrategyOptions.measurement,
210 measurement
211 )
083213c6 212 }
0804b9b4
JB
213 }
214 )
215 }
47ab5d23
JB
216 } else {
217 suite.add(
218 `${name} with ${workerChoiceStrategy} and ${
219 enableTasksQueue ? 'with' : 'without'
220 } tasks queue`,
221 async () => {
222 await runPoolifierPool(pool, {
223 taskExecutions,
224 workerData
225 })
226 },
227 {
228 onStart: () => {
229 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
230 pool.enableTasksQueue(enableTasksQueue)
231 strictEqual(
232 pool.opts.workerChoiceStrategy,
233 workerChoiceStrategy
234 )
235 strictEqual(pool.opts.enableTasksQueue, enableTasksQueue)
236 }
237 }
238 )
0804b9b4
JB
239 }
240 }
0804b9b4 241 }
47ab5d23
JB
242 suite
243 .on('complete', function () {
244 console.info(
245 'Fastest is ' +
246 LIST_FORMATTER.format(this.filter('fastest').map('name'))
247 )
248 })
249 .run({ async: true })
0804b9b4
JB
250 })
251}
252
b5ca7c94 253export const runPoolifierBenchmarkTatamiNg = async (
0804b9b4
JB
254 name,
255 workerType,
256 poolType,
257 poolSize,
258 { taskExecutions, workerData }
259) => {
260 try {
261 const pool = buildPoolifierPool(workerType, poolType, poolSize)
262 for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) {
263 for (const enableTasksQueue of [false, true]) {
264 if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) {
265 for (const measurement of [Measurements.runTime, Measurements.elu]) {
266 group(name, () => {
267 bench(
268 `${name} with ${workerChoiceStrategy}, with ${measurement} and ${
269 enableTasksQueue ? 'with' : 'without'
270 } tasks queue`,
271 async () => {
0804b9b4
JB
272 await runPoolifierPool(pool, {
273 taskExecutions,
274 workerData
275 })
2baf75cf
JB
276 },
277 {
278 before: () => {
279 pool.setWorkerChoiceStrategy(workerChoiceStrategy, {
280 measurement
281 })
282 pool.enableTasksQueue(enableTasksQueue)
283 strictEqual(
284 pool.opts.workerChoiceStrategy,
285 workerChoiceStrategy
286 )
287 strictEqual(pool.opts.enableTasksQueue, enableTasksQueue)
288 strictEqual(
289 pool.opts.workerChoiceStrategyOptions.measurement,
290 measurement
291 )
292 }
0804b9b4
JB
293 }
294 )
295 })
296 }
297 } else {
298 group(name, () => {
299 bench(
300 `${name} with ${workerChoiceStrategy} and ${
301 enableTasksQueue ? 'with' : 'without'
302 } tasks queue`,
303 async () => {
0804b9b4
JB
304 await runPoolifierPool(pool, {
305 taskExecutions,
306 workerData
307 })
2baf75cf
JB
308 },
309 {
310 before: () => {
311 pool.setWorkerChoiceStrategy(workerChoiceStrategy)
312 pool.enableTasksQueue(enableTasksQueue)
313 strictEqual(
314 pool.opts.workerChoiceStrategy,
315 workerChoiceStrategy
316 )
317 strictEqual(pool.opts.enableTasksQueue, enableTasksQueue)
318 }
0804b9b4
JB
319 }
320 )
321 })
322 }
323 }
324 }
ce711c4a
JB
325 await run({
326 json: env.CI != null ? 'bmf' : false
327 })
16534b42 328 clear()
2baf75cf 329 await pool.destroy()
0804b9b4
JB
330 } catch (error) {
331 console.error(error)
332 }
333}
334
335const LIST_FORMATTER = new Intl.ListFormat('en-US', {
336 style: 'long',
337 type: 'conjunction'
338})
a6bef8d2
JB
339
340export { executeTaskFunction }