From da3098610d6cf6155bbbe53c84b6ac6ccd400ff5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sun, 9 Apr 2023 12:53:59 +0200 Subject: [PATCH] feat: add pool option to enable median runtime MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .eslintrc.js | 1 - README.md | 6 ++++++ src/pools/abstract-pool.ts | 8 +++++++- src/pools/pool.ts | 9 ++++++++- .../abstract-worker-choice-strategy.ts | 16 ++++++++++++--- .../fair-share-worker-choice-strategy.ts | 7 ++++--- ...hted-round-robin-worker-choice-strategy.ts | 15 ++++++++++---- .../worker-choice-strategy-context.ts | 20 ++++++++++++------- tests/pools/abstract/abstract-pool.test.js | 7 +++++++ 9 files changed, 69 insertions(+), 20 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 70a8ec3c..ae288a7c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -49,7 +49,6 @@ module.exports = defineConfig({ 'esm', 'fibonacci', 'fs', - 'hrtime', 'inheritDoc', 'jsdoc', 'microjob', diff --git a/README.md b/README.md index 13c8c7fe..da169020 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,12 @@ Node versions >= 16.x are supported. `WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN` and `WorkerChoiceStrategies.FAIR_SHARE` strategies are targeted to heavy and long tasks Default: `WorkerChoiceStrategies.ROUND_ROBIN` +- `workerChoiceStrategyOptions` (optional) - The worker choice strategy options object to use in this pool. + Properties: + + - `medRunTime` (optional) - Use the tasks median run time instead of the tasks average run time in worker choice strategies. + Default: { medRunTime: false } + - `enableEvents` (optional) - Events emission enablement in this pool. Default: true - `enableTasksQueue` (optional, experimental) - Tasks queue per worker enablement in this pool. Default: false diff --git a/src/pools/abstract-pool.ts b/src/pools/abstract-pool.ts index 16688133..93516fee 100644 --- a/src/pools/abstract-pool.ts +++ b/src/pools/abstract-pool.ts @@ -93,7 +93,11 @@ export abstract class AbstractPool< Worker, Data, Response - >(this, this.opts.workerChoiceStrategy) + >( + this, + this.opts.workerChoiceStrategy, + this.opts.workerChoiceStrategyOptions + ) } private checkFilePath (filePath: string): void { @@ -127,6 +131,8 @@ export abstract class AbstractPool< this.opts.workerChoiceStrategy = opts.workerChoiceStrategy ?? WorkerChoiceStrategies.ROUND_ROBIN this.checkValidWorkerChoiceStrategy(this.opts.workerChoiceStrategy) + this.opts.workerChoiceStrategyOptions = + opts.workerChoiceStrategyOptions ?? { medRunTime: false } this.opts.enableEvents = opts.enableEvents ?? true this.opts.enableTasksQueue = opts.enableTasksQueue ?? false } diff --git a/src/pools/pool.ts b/src/pools/pool.ts index 9d18ed70..3c660031 100644 --- a/src/pools/pool.ts +++ b/src/pools/pool.ts @@ -5,7 +5,10 @@ import type { MessageHandler, OnlineHandler } from './worker' -import type { WorkerChoiceStrategy } from './selection-strategies/selection-strategies-types' +import type { + WorkerChoiceStrategy, + WorkerChoiceStrategyOptions +} from './selection-strategies/selection-strategies-types' /** * Pool events emitter. @@ -49,6 +52,10 @@ export interface PoolOptions { * The worker choice strategy to use in this pool. */ workerChoiceStrategy?: WorkerChoiceStrategy + /** + * The worker choice strategy options. + */ + workerChoiceStrategyOptions?: WorkerChoiceStrategyOptions /** * Pool events emission. * diff --git a/src/pools/selection-strategies/abstract-worker-choice-strategy.ts b/src/pools/selection-strategies/abstract-worker-choice-strategy.ts index 337b50b1..31041f3a 100644 --- a/src/pools/selection-strategies/abstract-worker-choice-strategy.ts +++ b/src/pools/selection-strategies/abstract-worker-choice-strategy.ts @@ -3,7 +3,8 @@ import { PoolType } from '../pool-internal' import type { IWorker } from '../worker' import type { IWorkerChoiceStrategy, - RequiredStatistics + RequiredStatistics, + WorkerChoiceStrategyOptions } from './selection-strategies-types' /** @@ -21,7 +22,7 @@ export abstract class AbstractWorkerChoiceStrategy< /** @inheritDoc */ protected readonly isDynamicPool: boolean /** @inheritDoc */ - public requiredStatistics: RequiredStatistics = { + public readonly requiredStatistics: RequiredStatistics = { runTime: false, avgRunTime: false, medRunTime: false @@ -31,14 +32,23 @@ export abstract class AbstractWorkerChoiceStrategy< * Constructs a worker choice strategy bound to the pool. * * @param pool - The pool instance. + * @param opts - The worker choice strategy options. */ public constructor ( - protected readonly pool: IPoolInternal + protected readonly pool: IPoolInternal, + protected readonly opts: WorkerChoiceStrategyOptions = { medRunTime: false } ) { + this.checkOptions() this.isDynamicPool = this.pool.type === PoolType.DYNAMIC this.choose.bind(this) } + private checkOptions (): void { + if (this.requiredStatistics.avgRunTime && this.opts.medRunTime === true) { + this.requiredStatistics.medRunTime = true + } + } + /** @inheritDoc */ public abstract reset (): boolean diff --git a/src/pools/selection-strategies/fair-share-worker-choice-strategy.ts b/src/pools/selection-strategies/fair-share-worker-choice-strategy.ts index c2f85f18..e1d123a9 100644 --- a/src/pools/selection-strategies/fair-share-worker-choice-strategy.ts +++ b/src/pools/selection-strategies/fair-share-worker-choice-strategy.ts @@ -88,11 +88,12 @@ export class FairShareWorkerChoiceStrategy< performance.now(), this.workerLastVirtualTaskTimestamp.get(workerNodeKey)?.end ?? -Infinity ) + const workerVirtualTaskTRunTime = this.requiredStatistics.medRunTime + ? this.pool.workerNodes[workerNodeKey].tasksUsage.medRunTime + : this.pool.workerNodes[workerNodeKey].tasksUsage.avgRunTime this.workerLastVirtualTaskTimestamp.set(workerNodeKey, { start: workerVirtualTaskStartTimestamp, - end: - workerVirtualTaskStartTimestamp + - (this.pool.workerNodes[workerNodeKey].tasksUsage.avgRunTime ?? 0) + end: workerVirtualTaskStartTimestamp + (workerVirtualTaskTRunTime ?? 0) }) } } diff --git a/src/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.ts b/src/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.ts index 1bdc02fb..c2782e8b 100644 --- a/src/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.ts +++ b/src/pools/selection-strategies/weighted-round-robin-worker-choice-strategy.ts @@ -4,7 +4,8 @@ import type { IWorker } from '../worker' import { AbstractWorkerChoiceStrategy } from './abstract-worker-choice-strategy' import type { IWorkerChoiceStrategy, - RequiredStatistics + RequiredStatistics, + WorkerChoiceStrategyOptions } from './selection-strategies-types' /** @@ -57,9 +58,13 @@ export class WeightedRoundRobinWorkerChoiceStrategy< * Constructs a worker choice strategy that selects with a weighted round robin scheduling algorithm. * * @param pool - The pool instance. + * @param opts - The worker choice strategy options. */ - public constructor (pool: IPoolInternal) { - super(pool) + public constructor ( + pool: IPoolInternal, + opts?: WorkerChoiceStrategyOptions + ) { + super(pool, opts) this.defaultWorkerWeight = this.computeWorkerWeight() this.initWorkersTaskRunTime() } @@ -146,7 +151,9 @@ export class WeightedRoundRobinWorkerChoiceStrategy< } private getWorkerVirtualTaskRunTime (workerNodeKey: number): number { - return this.pool.workerNodes[workerNodeKey].tasksUsage.avgRunTime + return this.requiredStatistics.medRunTime + ? this.pool.workerNodes[workerNodeKey].tasksUsage.medRunTime + : this.pool.workerNodes[workerNodeKey].tasksUsage.avgRunTime } private computeWorkerWeight (): number { diff --git a/src/pools/selection-strategies/worker-choice-strategy-context.ts b/src/pools/selection-strategies/worker-choice-strategy-context.ts index 0e5fd546..e763f7b2 100644 --- a/src/pools/selection-strategies/worker-choice-strategy-context.ts +++ b/src/pools/selection-strategies/worker-choice-strategy-context.ts @@ -7,7 +7,8 @@ import { RoundRobinWorkerChoiceStrategy } from './round-robin-worker-choice-stra import type { IWorkerChoiceStrategy, RequiredStatistics, - WorkerChoiceStrategy + WorkerChoiceStrategy, + WorkerChoiceStrategyOptions } from './selection-strategies-types' import { WorkerChoiceStrategies } from './selection-strategies-types' import { WeightedRoundRobinWorkerChoiceStrategy } from './weighted-round-robin-worker-choice-strategy' @@ -34,10 +35,12 @@ export class WorkerChoiceStrategyContext< * * @param pool - The pool instance. * @param workerChoiceStrategyType - The worker choice strategy. + * @param opts - The worker choice strategy options. */ public constructor ( pool: IPoolInternal, - private workerChoiceStrategyType: WorkerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN + private workerChoiceStrategyType: WorkerChoiceStrategy = WorkerChoiceStrategies.ROUND_ROBIN, + opts: WorkerChoiceStrategyOptions = { medRunTime: false } ) { this.execute.bind(this) this.workerChoiceStrategies = new Map< @@ -46,23 +49,26 @@ export class WorkerChoiceStrategyContext< >([ [ WorkerChoiceStrategies.ROUND_ROBIN, - new RoundRobinWorkerChoiceStrategy(pool) + new RoundRobinWorkerChoiceStrategy(pool, opts) ], [ WorkerChoiceStrategies.LESS_USED, - new LessUsedWorkerChoiceStrategy(pool) + new LessUsedWorkerChoiceStrategy(pool, opts) ], [ WorkerChoiceStrategies.LESS_BUSY, - new LessBusyWorkerChoiceStrategy(pool) + new LessBusyWorkerChoiceStrategy(pool, opts) ], [ WorkerChoiceStrategies.FAIR_SHARE, - new FairShareWorkerChoiceStrategy(pool) + new FairShareWorkerChoiceStrategy(pool, opts) ], [ WorkerChoiceStrategies.WEIGHTED_ROUND_ROBIN, - new WeightedRoundRobinWorkerChoiceStrategy(pool) + new WeightedRoundRobinWorkerChoiceStrategy( + pool, + opts + ) ] ]) } diff --git a/tests/pools/abstract/abstract-pool.test.js b/tests/pools/abstract/abstract-pool.test.js index 5f3617db..61188975 100644 --- a/tests/pools/abstract/abstract-pool.test.js +++ b/tests/pools/abstract/abstract-pool.test.js @@ -91,6 +91,9 @@ describe('Abstract pool test suite', () => { expect(pool.opts.workerChoiceStrategy).toBe( WorkerChoiceStrategies.ROUND_ROBIN ) + expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({ + medRunTime: false + }) expect(pool.opts.messageHandler).toBeUndefined() expect(pool.opts.errorHandler).toBeUndefined() expect(pool.opts.onlineHandler).toBeUndefined() @@ -102,6 +105,7 @@ describe('Abstract pool test suite', () => { './tests/worker-files/thread/testWorker.js', { workerChoiceStrategy: WorkerChoiceStrategies.LESS_USED, + workerChoiceStrategyOptions: { medRunTime: true }, enableEvents: false, enableTasksQueue: true, messageHandler: testHandler, @@ -116,6 +120,9 @@ describe('Abstract pool test suite', () => { expect(pool.opts.workerChoiceStrategy).toBe( WorkerChoiceStrategies.LESS_USED ) + expect(pool.opts.workerChoiceStrategyOptions).toStrictEqual({ + medRunTime: true + }) expect(pool.opts.messageHandler).toStrictEqual(testHandler) expect(pool.opts.errorHandler).toStrictEqual(testHandler) expect(pool.opts.onlineHandler).toStrictEqual(testHandler) -- 2.34.1