Skip to content

Commit

Permalink
feat(#305)!: Expose new PiscinaHistogram abstraction (#723)
Browse files Browse the repository at this point in the history
  • Loading branch information
metcoder95 authored Jan 10, 2025
1 parent 9fbb988 commit 86d736c
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 126 deletions.
84 changes: 84 additions & 0 deletions docs/docs/api-reference/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,90 @@ Use caution when setting resource limits. Setting limits that are too low may
result in the `Piscina` worker threads being unusable.
:::

## `PiscinaHistogram`

The `PiscinaHistogram` allows you to access the histogram data for the pool of worker threads.
It can be reset upon request in case of a need to clear the data.

**Example**:

```js
import { Piscina } from 'piscina';

const pool = new Piscina({
filename: resolve(__dirname, 'path/to/worker.js'),
});

const firstBatch = [];

for (let n = 0; n < 10; n++) {
firstBatch.push(pool.run('42'));
}

await Promise.all(firstBatch);

console.log(pool.histogram.runTime); // Print run time histogram summary
console.log(pool.histogram.waitTime); // Print wait time histogram summary

// If in need to reset the histogram data for a new set of tasks
pool.histogram.resetRunTime();
pool.histogram.resetWaitTime();

const secondBatch = [];

for (let n = 0; n < 10; n++) {
secondBatch.push(pool.run('42'));
}

await Promise.all(secondBatch);

// The histogram data will only contain the data for the second batch of tasks
console.log(pool.histogram.runTime);
console.log(pool.histogram.waitTime);
```

### Interface: `PiscinaLoadBalancer`

- `runTime`: (`PiscinaHistogramSummary`) Run Time Histogram Summary. Time taken to execute a task.
- `waitTime`: (`PiscinaHistogramSummary`) Wait Time Histogram Summary. Time between a task being submitted and the task starting to run.

> **Note**: The histogram data is only available if `recordTiming` is set to `true`.
```ts
type PiscinaHistogram = {
runTime: PiscinaHistogramSummary;
waitTime: PiscinaHistogramSummary;
resetRunTime(): void; // Reset Run Time Histogram
resetWaitTime(): void; // Reset Wait Time Histogram
```
### Interface: `PiscinaHistogramSummary`
```ts
type PiscinaHistogramSummary = {
average: number;
mean: number;
stddev: number;
min: number;
max: number;
p0_001: number;
p0_01: number;
p0_1: number;
p1: number;
p2_5: number;
p10: number;
p25: number;
p50: number;
p75: number;
p90: number;
p97_5: number;
p99: number;
p99_9: number;
p99_99: number;
p99_999: number;
}
```
## `PiscinaLoadBalancer`
The `PiscinaLoadBalancer` interface is used to implement custom load balancing algorithm that determines which worker thread should be assigned a task.
Expand Down
33 changes: 0 additions & 33 deletions src/common.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import type { Histogram } from 'node:perf_hooks';
import { fileURLToPath, URL } from 'node:url';
import { availableParallelism } from 'node:os';

import type { HistogramSummary } from './types';
import { kMovable, kTransferable, kValue } from './symbols';

// States wether the worker is ready to receive tasks
Expand Down Expand Up @@ -52,37 +50,6 @@ export const commonState = {
workerData: undefined
};

export function createHistogramSummary (histogram: Histogram): HistogramSummary {
const { mean, stddev, min, max } = histogram;

return {
average: mean / 1000,
mean: mean / 1000,
stddev,
min: min / 1000,
max: max / 1000,
p0_001: histogram.percentile(0.001) / 1000,
p0_01: histogram.percentile(0.01) / 1000,
p0_1: histogram.percentile(0.1) / 1000,
p1: histogram.percentile(1) / 1000,
p2_5: histogram.percentile(2.5) / 1000,
p10: histogram.percentile(10) / 1000,
p25: histogram.percentile(25) / 1000,
p50: histogram.percentile(50) / 1000,
p75: histogram.percentile(75) / 1000,
p90: histogram.percentile(90) / 1000,
p97_5: histogram.percentile(97.5) / 1000,
p99: histogram.percentile(99) / 1000,
p99_9: histogram.percentile(99.9) / 1000,
p99_99: histogram.percentile(99.99) / 1000,
p99_999: histogram.percentile(99.999) / 1000
};
}

export function toHistogramIntegerNano (milliseconds: number): number {
return Math.max(1, Math.trunc(milliseconds * 1000));
}

export function maybeFileURLToPath (filename : string) : string {
return filename.startsWith('file:')
? fileURLToPath(new URL(filename))
Expand Down
108 changes: 108 additions & 0 deletions src/histogram.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { RecordableHistogram, createHistogram } from 'node:perf_hooks';

export type PiscinaHistogramSummary = {
average: number;
mean: number;
stddev: number;
min: number;
max: number;
p0_001: number;
p0_01: number;
p0_1: number;
p1: number;
p2_5: number;
p10: number;
p25: number;
p50: number;
p75: number;
p90: number;
p97_5: number;
p99: number;
p99_9: number;
p99_99: number;
p99_999: number;
};

export type PiscinaHistogram = {
runTime: PiscinaHistogramSummary;
waitTime: PiscinaHistogramSummary;
resetRunTime(): void;
resetWaitTime(): void;
};

export class PiscinaHistogramHandler {
#runTime: RecordableHistogram;
#waitTime: RecordableHistogram;

constructor() {
this.#runTime = createHistogram();
this.#waitTime = createHistogram();
}

get runTimeSummary(): PiscinaHistogramSummary {
return PiscinaHistogramHandler.createHistogramSummary(this.#runTime);
}

get waitTimeSummary(): PiscinaHistogramSummary {
return PiscinaHistogramHandler.createHistogramSummary(this.#waitTime);
}

get runTimeCount(): number {
return this.#runTime.count;
}

get waitTimeCount(): number {
return this.#waitTime.count;
}

recordRunTime(value: number) {
this.#runTime.record(PiscinaHistogramHandler.toHistogramIntegerNano(value));
}

recordWaitTime(value: number) {
this.#waitTime.record(
PiscinaHistogramHandler.toHistogramIntegerNano(value)
);
}

resetWaitTime(): void {
this.#waitTime.reset();
}

resetRunTime(): void {
this.#runTime.reset();
}

static createHistogramSummary(
histogram: RecordableHistogram
): PiscinaHistogramSummary {
const { mean, stddev, min, max } = histogram;

return {
average: mean / 1000,
mean: mean / 1000,
stddev,
min: min / 1000,
max: max / 1000,
p0_001: histogram.percentile(0.001) / 1000,
p0_01: histogram.percentile(0.01) / 1000,
p0_1: histogram.percentile(0.1) / 1000,
p1: histogram.percentile(1) / 1000,
p2_5: histogram.percentile(2.5) / 1000,
p10: histogram.percentile(10) / 1000,
p25: histogram.percentile(25) / 1000,
p50: histogram.percentile(50) / 1000,
p75: histogram.percentile(75) / 1000,
p90: histogram.percentile(90) / 1000,
p97_5: histogram.percentile(97.5) / 1000,
p99: histogram.percentile(99) / 1000,
p99_9: histogram.percentile(99.9) / 1000,
p99_99: histogram.percentile(99.99) / 1000,
p99_999: histogram.percentile(99.999) / 1000,
};
}

static toHistogramIntegerNano(milliseconds: number): number {
return Math.max(1, Math.trunc(milliseconds * 1000));
}
}
Loading

0 comments on commit 86d736c

Please sign in to comment.