CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-piscina

A fast, efficient Node.js Worker Thread Pool implementation

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

performance-monitoring.mddocs/

Performance Monitoring

Built-in performance metrics collection with detailed histogram statistics for runtime and wait times, including percentile calculations.

Capabilities

PiscinaHistogram Interface

Main interface for accessing performance metrics from the pool.

/**
 * Main histogram interface providing runtime and wait time statistics
 */
interface PiscinaHistogram {
  /** Runtime statistics for task execution */
  readonly runTime: PiscinaHistogramSummary;
  
  /** Wait time statistics for task queuing */
  readonly waitTime: PiscinaHistogramSummary;
  
  /**
   * Reset runtime histogram data
   */
  resetRunTime(): void;
  
  /**
   * Reset wait time histogram data
   */
  resetWaitTime(): void;
}

PiscinaHistogramSummary Interface

Detailed statistical summary with percentile calculations.

/**
 * Comprehensive statistical summary of timing data
 * All values are in milliseconds
 */
interface PiscinaHistogramSummary {
  /** Average/mean execution time in milliseconds */
  readonly average: number;
  
  /** Mean execution time in milliseconds (same as average) */
  readonly mean: number;
  
  /** Standard deviation of execution times */
  readonly stddev: number;
  
  /** Minimum recorded execution time */
  readonly min: number;
  
  /** Maximum recorded execution time */
  readonly max: number;
  
  // Percentile values (all in milliseconds)
  /** 0.1st percentile (1 in 1000 tasks faster) */
  readonly p0_001: number;
  
  /** 1st percentile (1 in 100 tasks faster) */
  readonly p0_01: number;
  
  /** 10th percentile (1 in 10 tasks faster) */
  readonly p0_1: number;
  
  /** 1st percentile */
  readonly p1: number;
  
  /** 2.5th percentile */
  readonly p2_5: number;
  
  /** 10th percentile */
  readonly p10: number;
  
  /** 25th percentile (first quartile) */
  readonly p25: number;
  
  /** 50th percentile (median) */
  readonly p50: number;
  
  /** 75th percentile (third quartile) */
  readonly p75: number;
  
  /** 90th percentile */
  readonly p90: number;
  
  /** 97.5th percentile */
  readonly p97_5: number;
  
  /** 99th percentile */
  readonly p99: number;
  
  /** 99.9th percentile */
  readonly p99_9: number;
  
  /** 99.99th percentile */
  readonly p99_99: number;
  
  /** 99.999th percentile */
  readonly p99_999: number;
}

Pool-Level Metrics

Access performance metrics through the main pool instance.

class Piscina {
  /** Performance histogram (when recordTiming is enabled) */
  readonly histogram: PiscinaHistogram;
  
  /** Pool utilization as percentage (0-1) */
  readonly utilization: number;
  
  /** Pool runtime duration in milliseconds */
  readonly duration: number;
  
  /** Total number of completed tasks */
  readonly completed: number;
}

Usage Examples:

import Piscina from "piscina";

// Create pool with timing enabled (default)
const pool = new Piscina({
  filename: "worker.js",
  recordTiming: true, // Default: true
  workerHistogram: true // Enable per-worker histograms
});

// Run some tasks
await Promise.all([
  pool.run({ task: "fast" }),
  pool.run({ task: "medium" }),
  pool.run({ task: "slow" })
]);

// Access performance metrics
const stats = pool.histogram;

console.log('Runtime Statistics:');
console.log(`Average: ${stats.runTime.average.toFixed(2)}ms`);
console.log(`Median (p50): ${stats.runTime.p50.toFixed(2)}ms`);
console.log(`97.5th percentile: ${stats.runTime.p97_5.toFixed(2)}ms`);
console.log(`99th percentile: ${stats.runTime.p99.toFixed(2)}ms`);
console.log(`Min: ${stats.runTime.min.toFixed(2)}ms`);
console.log(`Max: ${stats.runTime.max.toFixed(2)}ms`);

console.log('\nWait Time Statistics:');
console.log(`Average wait: ${stats.waitTime.average.toFixed(2)}ms`);
console.log(`Median wait: ${stats.waitTime.p50.toFixed(2)}ms`);
console.log(`99th percentile wait: ${stats.waitTime.p99.toFixed(2)}ms`);

console.log('\nPool Statistics:');
console.log(`Utilization: ${(pool.utilization * 100).toFixed(2)}%`);
console.log(`Completed tasks: ${pool.completed}`);
console.log(`Running for: ${pool.duration.toFixed(2)}ms`);

Worker-Level Metrics

Individual worker performance tracking when workerHistogram is enabled.

interface PiscinaWorker {
  /** Worker-specific histogram (when workerHistogram is enabled) */
  readonly histogram: PiscinaHistogramSummary | null;
}

Usage Examples:

import Piscina from "piscina";

const pool = new Piscina({
  filename: "worker.js",
  workerHistogram: true, // Enable per-worker tracking
  maxThreads: 4
});

// Run tasks and monitor per-worker performance
await Promise.all(
  Array.from({ length: 100 }, (_, i) => pool.run({ taskId: i }))
);

// Monitor individual worker performance
pool.threads.forEach((thread, index) => {
  const workerStats = thread.histogram;
  if (workerStats) {
    console.log(`\nWorker ${index} Performance:`);
    console.log(`Average runtime: ${workerStats.average.toFixed(2)}ms`);
    console.log(`Median runtime: ${workerStats.p50.toFixed(2)}ms`);
    console.log(`97.5th percentile: ${workerStats.p97_5.toFixed(2)}ms`);
  }
});

Performance Monitoring Patterns

Real-time Monitoring

import Piscina from "piscina";

const pool = new Piscina({
  filename: "worker.js",
  recordTiming: true,
  workerHistogram: true
});

// Real-time performance dashboard
function displayPerformanceMetrics() {
  const stats = pool.histogram;
  
  console.clear();
  console.log('=== Piscina Performance Dashboard ===\n');
  
  // Pool overview
  console.log(`Pool Utilization: ${(pool.utilization * 100).toFixed(1)}%`);
  console.log(`Active Threads: ${pool.threads.length}`);
  console.log(`Queue Size: ${pool.queueSize}`);
  console.log(`Completed Tasks: ${pool.completed}`);
  console.log(`Pool Runtime: ${(pool.duration / 1000).toFixed(1)}s\n`);
  
  // Runtime statistics
  console.log('Runtime Statistics:');
  console.log(`  Mean: ${stats.runTime.mean.toFixed(2)}ms`);
  console.log(`  Median: ${stats.runTime.p50.toFixed(2)}ms`);
  console.log(`  P97.5: ${stats.runTime.p97_5.toFixed(2)}ms`);
  console.log(`  P99: ${stats.runTime.p99.toFixed(2)}ms\n`);
  
  // Wait time statistics
  console.log('Wait Time Statistics:');
  console.log(`  Mean: ${stats.waitTime.mean.toFixed(2)}ms`);
  console.log(`  Median: ${stats.waitTime.p50.toFixed(2)}ms`);
  console.log(`  P97.5: ${stats.waitTime.p97_5.toFixed(2)}ms`);
  console.log(`  P99: ${stats.waitTime.p99.toFixed(2)}ms\n`);
  
  // Worker breakdown
  console.log('Worker Performance:');
  pool.threads.forEach((thread, index) => {
    const usage = thread.currentUsage;
    const hist = thread.histogram;
    const avgTime = hist ? hist.average.toFixed(1) : 'N/A';
    console.log(`  Worker ${index}: ${usage} tasks, ${avgTime}ms avg`);
  });
}

// Update dashboard every 2 seconds
const dashboardInterval = setInterval(displayPerformanceMetrics, 2000);

// Stop monitoring
setTimeout(() => {
  clearInterval(dashboardInterval);
  pool.close();
}, 60000);

Performance Alerting

import Piscina from "piscina";

const pool = new Piscina({
  filename: "worker.js",
  recordTiming: true
});

// Performance thresholds
const THRESHOLDS = {
  avgRuntimeMs: 1000,    // Alert if average > 1s
  p99RuntimeMs: 5000,    // Alert if P99 > 5s
  avgWaitTimeMs: 100,    // Alert if wait > 100ms
  utilizationPercent: 90  // Alert if utilization > 90%
};

function checkPerformanceAlerts() {
  const stats = pool.histogram;
  const utilization = pool.utilization * 100;
  
  // Runtime alerts
  if (stats.runTime.average > THRESHOLDS.avgRuntimeMs) {
    console.warn(`⚠️  High average runtime: ${stats.runTime.average.toFixed(2)}ms`);
  }
  
  if (stats.runTime.p99 > THRESHOLDS.p99RuntimeMs) {
    console.warn(`⚠️  High P99 runtime: ${stats.runTime.p99.toFixed(2)}ms`);
  }
  
  // Wait time alerts
  if (stats.waitTime.average > THRESHOLDS.avgWaitTimeMs) {
    console.warn(`⚠️  High average wait time: ${stats.waitTime.average.toFixed(2)}ms`);
  }
  
  // Utilization alerts
  if (utilization > THRESHOLDS.utilizationPercent) {
    console.warn(`⚠️  High utilization: ${utilization.toFixed(1)}%`);
  }
  
  // Queue backup alert
  if (pool.queueSize > 10) {
    console.warn(`⚠️  Queue backup: ${pool.queueSize} tasks waiting`);
  }
}

// Check for alerts every 10 seconds
const alertInterval = setInterval(checkPerformanceAlerts, 10000);

Performance Logging

import Piscina from "piscina";
import { writeFileSync, appendFileSync } from "fs";

const pool = new Piscina({
  filename: "worker.js",
  recordTiming: true,
  workerHistogram: true
});

// Log performance metrics to file
function logPerformanceMetrics() {
  const timestamp = new Date().toISOString();
  const stats = pool.histogram;
  
  const logEntry = {
    timestamp,
    utilization: pool.utilization,
    completed: pool.completed,
    queueSize: pool.queueSize,
    activeThreads: pool.threads.length,
    runtime: {
      mean: stats.runTime.mean,
      median: stats.runTime.p50,
      p97_5: stats.runTime.p97_5,
      p99: stats.runTime.p99
    },
    waitTime: {
      mean: stats.waitTime.mean,
      median: stats.waitTime.p50,
      p97_5: stats.waitTime.p97_5,
      p99: stats.waitTime.p99
    }
  };
  
  appendFileSync('piscina-performance.log', JSON.stringify(logEntry) + '\n');
}

// Log metrics every minute
const logInterval = setInterval(logPerformanceMetrics, 60000);

// Reset metrics periodically to avoid memory growth
setInterval(() => {
  console.log('Resetting performance metrics...');
  pool.histogram.resetRunTime();
  pool.histogram.resetWaitTime();
}, 3600000); // Reset every hour

Histogram Reset Operations

Reset performance data for fresh measurements.

interface PiscinaHistogram {
  /**
   * Reset all runtime measurement data
   * Useful for periodic cleanup or benchmarking specific periods
   */
  resetRunTime(): void;
  
  /**
   * Reset all wait time measurement data
   * Useful for periodic cleanup or benchmarking specific periods
   */
  resetWaitTime(): void;
}

Usage Examples:

const pool = new Piscina({ filename: "worker.js" });

// Run baseline measurements
await runBaselineTasks();

// Reset for clean benchmark
pool.histogram.resetRunTime();
pool.histogram.resetWaitTime();

// Run benchmark
await runBenchmarkTasks();

// Get clean benchmark results
const benchmarkStats = pool.histogram;
console.log(`Benchmark average: ${benchmarkStats.runTime.average}ms`);

Configuration Options

Control performance monitoring behavior through pool options.

interface Options {
  /** Enable/disable timing collection (default: true) */
  recordTiming?: boolean;
  
  /** Enable per-worker histograms (default: false) */
  workerHistogram?: boolean;
}

Usage Examples:

// Disable performance monitoring for maximum performance
const highPerformancePool = new Piscina({
  filename: "worker.js",
  recordTiming: false, // Disable metrics collection
  workerHistogram: false
});

// Full monitoring for development/debugging
const debugPool = new Piscina({
  filename: "worker.js",
  recordTiming: true,
  workerHistogram: true
});

// Production monitoring (pool-level only)
const productionPool = new Piscina({
  filename: "worker.js",
  recordTiming: true,
  workerHistogram: false // Reduce overhead
});

Install with Tessl CLI

npx tessl i tessl/npm-piscina

docs

index.md

load-balancing.md

performance-monitoring.md

pool-management.md

task-cancellation.md

task-queues.md

transferable-objects.md

tile.json