X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=benchmarks%2Fbenchmarks-utils.mjs;h=35c335c45877748ec332e45cd10fda5e5d4b69df;hb=a191d3fbb41de19e0175fe793fa3590c7d8705ab;hp=2d553580c42e29491d8dc7efbfa4750482f1dc15;hpb=cde5b54ed8ffa2a288b697c3ab921a6cf7231694;p=poolifier.git diff --git a/benchmarks/benchmarks-utils.mjs b/benchmarks/benchmarks-utils.mjs index 2d553580..35c335c4 100644 --- a/benchmarks/benchmarks-utils.mjs +++ b/benchmarks/benchmarks-utils.mjs @@ -1,24 +1,21 @@ -import crypto from 'node:crypto' -import assert from 'node:assert' -import fs from 'node:fs' +import { strictEqual } from 'node:assert' + import Benchmark from 'benchmark' +import { bench, clear, group, run } from 'tatami-ng' + import { DynamicClusterPool, DynamicThreadPool, FixedClusterPool, FixedThreadPool, + Measurements, PoolTypes, WorkerChoiceStrategies, WorkerTypes } from '../lib/index.mjs' -import { TaskFunctions } from './benchmarks-types.mjs' +import { executeTaskFunction } from './benchmarks-utils.cjs' -export const buildPoolifierPool = ( - workerType, - poolType, - poolSize, - poolOptions -) => { +const buildPoolifierPool = (workerType, poolType, poolSize, poolOptions) => { switch (poolType) { case PoolTypes.fixed: switch (workerType) { @@ -31,7 +28,7 @@ export const buildPoolifierPool = ( case WorkerTypes.cluster: return new FixedClusterPool( poolSize, - './benchmarks/internal/cluster-worker.mjs', + './benchmarks/internal/cluster-worker.cjs', poolOptions ) } @@ -49,7 +46,7 @@ export const buildPoolifierPool = ( return new DynamicClusterPool( Math.floor(poolSize / 2), poolSize, - './benchmarks/internal/cluster-worker.mjs', + './benchmarks/internal/cluster-worker.cjs', poolOptions ) } @@ -57,10 +54,7 @@ export const buildPoolifierPool = ( } } -export const runPoolifierPool = async ( - pool, - { taskExecutions, workerData } -) => { +const runPoolifierPool = async (pool, { taskExecutions, workerData }) => { return await new Promise((resolve, reject) => { let executions = 0 for (let i = 1; i <= taskExecutions; i++) { @@ -71,7 +65,7 @@ export const runPoolifierPool = async ( if (executions === taskExecutions) { resolve({ ok: 1 }) } - return null + return undefined }) .catch(err => { console.error(err) @@ -81,152 +75,263 @@ export const runPoolifierPool = async ( }) } -export const runPoolifierPoolBenchmark = async ( +export const runPoolifierBenchmarkBenchmarkJs = async ( name, - pool, + workerType, + poolType, + poolSize, + poolOptions, { taskExecutions, workerData } ) => { return await new Promise((resolve, reject) => { - try { - const suite = new Benchmark.Suite(name) - for (const workerChoiceStrategy of Object.values( - WorkerChoiceStrategies - )) { - for (const enableTasksQueue of [false, true]) { + const pool = buildPoolifierPool(workerType, poolType, poolSize, poolOptions) + let workerChoiceStrategy + let enableTasksQueue + let workerChoiceStrategyOptions + if (poolOptions != null) { + ({ + workerChoiceStrategy, + enableTasksQueue, + workerChoiceStrategyOptions + } = poolOptions) + } + const measurement = workerChoiceStrategyOptions?.measurement + new Benchmark( + `${name} with ${workerChoiceStrategy ?? pool.opts.workerChoiceStrategy}${ + measurement != null ? `, with ${measurement}` : '' + } and ${enableTasksQueue ? 'with' : 'without'} tasks queue`, + async () => { + await runPoolifierPool(pool, { + taskExecutions, + workerData + }) + }, + { + onStart: () => { + if (workerChoiceStrategy != null) { + strictEqual(pool.opts.workerChoiceStrategy, workerChoiceStrategy) + } + if (enableTasksQueue != null) { + strictEqual(pool.opts.enableTasksQueue, enableTasksQueue) + } + if (measurement != null) { + strictEqual( + pool.opts.workerChoiceStrategyOptions.measurement, + measurement + ) + } + }, + onComplete: event => { + console.info(event.target.toString()) + if (pool.started && !pool.destroying) { + pool.destroy().then(resolve).catch(reject) + } else { + resolve() + } + }, + onError: event => { + if (pool.started && !pool.destroying) { + pool + .destroy() + .then(() => { + return reject(event.target.error) + }) + .catch(() => {}) + } else { + reject(event.target.error) + } + } + } + ).run({ async: true }) + }) +} + +export const runPoolifierBenchmarkBenchmarkJsSuite = async ( + name, + workerType, + poolType, + poolSize, + { taskExecutions, workerData } +) => { + return await new Promise((resolve, reject) => { + const pool = buildPoolifierPool(workerType, poolType, poolSize) + const suite = new Benchmark.Suite(name, { + onComplete: () => { + if (pool.started && !pool.destroying) { + pool.destroy().then(resolve).catch(reject) + } else { + resolve() + } + }, + onCycle: event => { + console.info(event.target.toString()) + }, + onError: event => { + if (pool.started && !pool.destroying) { + pool + .destroy() + .then(() => { + return reject(event.target.error) + }) + .catch(() => {}) + } else { + reject(event.target.error) + } + } + }) + for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) { + for (const enableTasksQueue of [false, true]) { + if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) { + for (const measurement of [Measurements.runTime, Measurements.elu]) { + suite.add( + `${name} with ${workerChoiceStrategy}, with ${measurement} and ${ + enableTasksQueue ? 'with' : 'without' + } tasks queue`, + async () => { + await runPoolifierPool(pool, { + taskExecutions, + workerData + }) + }, + { + onStart: () => { + pool.setWorkerChoiceStrategy(workerChoiceStrategy, { + measurement + }) + pool.enableTasksQueue(enableTasksQueue) + strictEqual( + pool.opts.workerChoiceStrategy, + workerChoiceStrategy + ) + strictEqual(pool.opts.enableTasksQueue, enableTasksQueue) + strictEqual( + pool.opts.workerChoiceStrategyOptions.measurement, + measurement + ) + } + } + ) + } + } else { suite.add( - `${name}|${workerChoiceStrategy}|${ + `${name} with ${workerChoiceStrategy} and ${ enableTasksQueue ? 'with' : 'without' } tasks queue`, async () => { - pool.setWorkerChoiceStrategy(workerChoiceStrategy) - pool.enableTasksQueue(enableTasksQueue) - assert.strictEqual( - pool.opts.workerChoiceStrategy, - workerChoiceStrategy - ) - assert.strictEqual(pool.opts.enableTasksQueue, enableTasksQueue) await runPoolifierPool(pool, { taskExecutions, workerData }) + }, + { + onStart: () => { + pool.setWorkerChoiceStrategy(workerChoiceStrategy) + pool.enableTasksQueue(enableTasksQueue) + strictEqual( + pool.opts.workerChoiceStrategy, + workerChoiceStrategy + ) + strictEqual(pool.opts.enableTasksQueue, enableTasksQueue) + } } ) } } - suite - .on('cycle', event => { - console.info(event.target.toString()) - }) - .on('complete', async function () { - console.info( - 'Fastest is ' + - LIST_FORMATTER.format(this.filter('fastest').map('name')) - ) - await pool.destroy() - resolve() - }) - .run({ async: true }) - } catch (error) { - reject(error) } + suite + .on('complete', function () { + console.info( + 'Fastest is ' + + LIST_FORMATTER.format(this.filter('fastest').map('name')) + ) + }) + .run({ async: true }) }) } -export const LIST_FORMATTER = new Intl.ListFormat('en-US', { - style: 'long', - type: 'conjunction' -}) - -export const executeAsyncFn = async fn => { - try { - await fn() - } catch (e) { - console.error(e) - // eslint-disable-next-line n/no-process-exit - process.exit(1) - } -} - -export const generateRandomInteger = ( - max = Number.MAX_SAFE_INTEGER, - min = 0 +export const runPoolifierBenchmarkTatamiNg = async ( + name, + workerType, + poolType, + poolSize, + { taskExecutions, workerData } ) => { - if (max < min || max < 0 || min < 0) { - throw new RangeError('Invalid interval') - } - max = Math.floor(max) - if (min != null && min !== 0) { - min = Math.ceil(min) - return Math.floor(Math.random() * (max - min + 1)) + min - } - return Math.floor(Math.random() * (max + 1)) -} - -const jsonIntegerSerialization = n => { - for (let i = 0; i < n; i++) { - const o = { - a: i + try { + const pool = buildPoolifierPool(workerType, poolType, poolSize) + for (const workerChoiceStrategy of Object.values(WorkerChoiceStrategies)) { + for (const enableTasksQueue of [false, true]) { + if (workerChoiceStrategy === WorkerChoiceStrategies.FAIR_SHARE) { + for (const measurement of [Measurements.runTime, Measurements.elu]) { + group(name, () => { + bench( + `${name} with ${workerChoiceStrategy}, with ${measurement} and ${ + enableTasksQueue ? 'with' : 'without' + } tasks queue`, + async () => { + await runPoolifierPool(pool, { + taskExecutions, + workerData + }) + }, + { + before: () => { + pool.setWorkerChoiceStrategy(workerChoiceStrategy, { + measurement + }) + pool.enableTasksQueue(enableTasksQueue) + strictEqual( + pool.opts.workerChoiceStrategy, + workerChoiceStrategy + ) + strictEqual(pool.opts.enableTasksQueue, enableTasksQueue) + strictEqual( + pool.opts.workerChoiceStrategyOptions.measurement, + measurement + ) + } + } + ) + }) + } + } else { + group(name, () => { + bench( + `${name} with ${workerChoiceStrategy} and ${ + enableTasksQueue ? 'with' : 'without' + } tasks queue`, + async () => { + await runPoolifierPool(pool, { + taskExecutions, + workerData + }) + }, + { + before: () => { + pool.setWorkerChoiceStrategy(workerChoiceStrategy) + pool.enableTasksQueue(enableTasksQueue) + strictEqual( + pool.opts.workerChoiceStrategy, + workerChoiceStrategy + ) + strictEqual(pool.opts.enableTasksQueue, enableTasksQueue) + } + } + ) + }) + } + } } - JSON.stringify(o) - } - return { ok: 1 } -} - -/** - * Intentionally inefficient implementation. - * @param {number} n - The number of fibonacci numbers to generate. - * @returns {number} - The nth fibonacci number. - */ -const fibonacci = n => { - if (n <= 1) return n - return fibonacci(n - 1) + fibonacci(n - 2) -} - -/** - * Intentionally inefficient implementation. - * @param {number} n - The number to calculate the factorial of. - * @returns {number} - The factorial of n. - */ -const factorial = n => { - if (n === 0) { - return 1 + await run() + clear() + await pool.destroy() + } catch (error) { + console.error(error) } - return factorial(n - 1) * n } -const readWriteFiles = ( - n, - baseDirectory = `/tmp/poolifier-benchmarks/${crypto.randomInt( - 281474976710655 - )}` -) => { - if (fs.existsSync(baseDirectory) === true) { - fs.rmSync(baseDirectory, { recursive: true }) - } - fs.mkdirSync(baseDirectory, { recursive: true }) - for (let i = 0; i < n; i++) { - const filePath = `${baseDirectory}/${i}` - fs.writeFileSync(filePath, i.toString(), { - encoding: 'utf8', - flag: 'a' - }) - fs.readFileSync(filePath, 'utf8') - } - fs.rmSync(baseDirectory, { recursive: true }) - return { ok: 1 } -} +const LIST_FORMATTER = new Intl.ListFormat('en-US', { + style: 'long', + type: 'conjunction' +}) -export const executeTaskFunction = data => { - switch (data.function) { - case TaskFunctions.jsonIntegerSerialization: - return jsonIntegerSerialization(data.taskSize || 1000) - case TaskFunctions.fibonacci: - return fibonacci(data.taskSize || 1000) - case TaskFunctions.factorial: - return factorial(data.taskSize || 1000) - case TaskFunctions.readWriteFiles: - return readWriteFiles(data.taskSize || 1000) - default: - throw new Error('Unknown task function') - } -} +export { executeTaskFunction }