docs: add scope on pool public API
[poolifier.git] / src / utils.ts
CommitLineData
aa4bf4b2 1import * as os from 'node:os'
3c93feb9
JB
2import type {
3 MeasurementStatisticsRequirements,
4 WorkerChoiceStrategyOptions
5} from './pools/selection-strategies/selection-strategies-types'
59317253 6import type { KillBehavior } from './worker/worker-options'
e4f20deb 7import type { MeasurementStatistics } from './pools/worker'
bbeadd16 8
ff128cc9
JB
9/**
10 * Default task name.
11 */
12export const DEFAULT_TASK_NAME = 'default'
13
6e9d10db
JB
14/**
15 * An intentional empty function.
16 */
4f3c3d89 17export const EMPTY_FUNCTION: () => void = Object.freeze(() => {
6e9d10db 18 /* Intentionally empty */
4f3c3d89 19})
78099a15
JB
20
21/**
bbeadd16
JB
22 * Default worker choice strategy options.
23 */
24export const DEFAULT_WORKER_CHOICE_STRATEGY_OPTIONS: WorkerChoiceStrategyOptions =
25 {
932fc8be 26 runTime: { median: false },
5df69fab
JB
27 waitTime: { median: false },
28 elu: { median: false }
bbeadd16
JB
29 }
30
3c93feb9
JB
31/**
32 * Default measurement statistics requirements.
33 */
34export const DEFAULT_MEASUREMENT_STATISTICS_REQUIREMENTS: MeasurementStatisticsRequirements =
35 {
36 aggregate: false,
37 average: false,
38 median: false
39 }
40
51474716 41/**
ab80dc46
JB
42 * Returns safe host OS optimized estimate of the default amount of parallelism a pool should use.
43 * Always returns a value greater than zero.
44 *
45 * @returns The host OS optimized maximum pool size.
51474716
JB
46 */
47export const availableParallelism = (): number => {
48 let availableParallelism = 1
49 try {
aa4bf4b2 50 availableParallelism = os.availableParallelism()
51474716 51 } catch {
aa4bf4b2 52 const numberOfCpus = os.cpus()
2845f2a5
JB
53 if (Array.isArray(numberOfCpus) && numberOfCpus.length > 0) {
54 availableParallelism = numberOfCpus.length
51474716
JB
55 }
56 }
57 return availableParallelism
58}
59
bbeadd16 60/**
afe0d5bf 61 * Computes the median of the given data set.
78099a15
JB
62 *
63 * @param dataSet - Data set.
64 * @returns The median of the given data set.
65 */
66export const median = (dataSet: number[]): number => {
4a45e8d2
JB
67 if (Array.isArray(dataSet) && dataSet.length === 0) {
68 return 0
69 }
78099a15
JB
70 if (Array.isArray(dataSet) && dataSet.length === 1) {
71 return dataSet[0]
72 }
c6f42dd6
JB
73 const sortedDataSet = dataSet.slice().sort((a, b) => a - b)
74 return (
75 (sortedDataSet[(sortedDataSet.length - 1) >> 1] +
76 sortedDataSet[sortedDataSet.length >> 1]) /
77 2
78 )
78099a15 79}
0d80593b 80
afe0d5bf
JB
81/**
82 * Rounds the given number to the given scale.
64383951 83 * The rounding is done using the "round half away from zero" method.
afe0d5bf
JB
84 *
85 * @param num - The number to round.
86 * @param scale - The scale to round to.
87 * @returns The rounded number.
88 */
89export const round = (num: number, scale = 2): number => {
90 const rounder = Math.pow(10, scale)
91 return Math.round(num * rounder * (1 + Number.EPSILON)) / rounder
92}
93
3c653a03
JB
94/**
95 * Is the given object a plain object?
96 *
97 * @param obj - The object to check.
98 * @returns `true` if the given object is a plain object, `false` otherwise.
99 */
0d80593b
JB
100export const isPlainObject = (obj: unknown): boolean =>
101 typeof obj === 'object' &&
102 obj !== null &&
103 obj?.constructor === Object &&
104 Object.prototype.toString.call(obj) === '[object Object]'
59317253
JB
105
106/**
107 * Detects whether the given value is a kill behavior or not.
108 *
109 * @typeParam KB - Which specific KillBehavior type to test against.
110 * @param killBehavior - Which kind of kill behavior to detect.
111 * @param value - Any value.
112 * @returns `true` if `value` was strictly equals to `killBehavior`, otherwise `false`.
113 */
114export const isKillBehavior = <KB extends KillBehavior>(
115 killBehavior: KB,
116 value: unknown
117): value is KB => {
118 return value === killBehavior
119}
49d1b48c
JB
120
121/**
122 * Detects whether the given value is an asynchronous function or not.
123 *
124 * @param fn - Any value.
125 * @returns `true` if `fn` was an asynchronous function, otherwise `false`.
126 */
127export const isAsyncFunction = (
128 fn: unknown
129): fn is (...args: unknown[]) => Promise<unknown> => {
130 return typeof fn === 'function' && fn.constructor.name === 'AsyncFunction'
131}
e4f20deb
JB
132
133/**
134 * Updates the given measurement statistics.
135 *
136 * @param measurementStatistics - The measurement statistics to update.
137 * @param measurementRequirements - The measurement statistics requirements.
138 * @param measurementValue - The measurement value.
008512c7 139 * @param numberOfMeasurements - The number of measurements.
e4f20deb
JB
140 */
141export const updateMeasurementStatistics = (
142 measurementStatistics: MeasurementStatistics,
143 measurementRequirements: MeasurementStatisticsRequirements,
144 measurementValue: number,
008512c7 145 numberOfMeasurements: number
e4f20deb
JB
146): void => {
147 if (measurementRequirements.aggregate) {
148 measurementStatistics.aggregate =
149 (measurementStatistics.aggregate ?? 0) + measurementValue
150 measurementStatistics.minimum = Math.min(
151 measurementValue,
152 measurementStatistics.minimum ?? Infinity
153 )
154 measurementStatistics.maximum = Math.max(
155 measurementValue,
156 measurementStatistics.maximum ?? -Infinity
157 )
008512c7 158 if (measurementRequirements.average && numberOfMeasurements !== 0) {
e4f20deb 159 measurementStatistics.average =
008512c7 160 measurementStatistics.aggregate / numberOfMeasurements
e4f20deb
JB
161 }
162 if (measurementRequirements.median && measurementValue != null) {
163 measurementStatistics.history.push(measurementValue)
164 measurementStatistics.median = median(measurementStatistics.history)
165 }
166 }
167}