Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 181 additions & 0 deletions benchmark/timers/priority-queue-realistic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
'use strict';

// REALISTIC Node.js timer queue benchmark
// Tests actual production workload: small queue (3-15 items), frequent peek()

const common = require('../common.js');

const bench = common.createBenchmark(main, {
queueSize: [3, 5, 10, 15, 20],
impl: ['original', 'adaptive'],
workload: ['web-server', 'microservice', 'real-time'],
});

function createHeap(impl, comparator, setPosition) {
if (impl === 'original') {
const PriorityQueue = require('../../lib/internal/priority_queue');
return new PriorityQueue(comparator, setPosition);
} else if (impl === 'adaptive') {
const PriorityQueueAdaptive = require('../../lib/internal/priority_queue_adaptive');
return new PriorityQueueAdaptive(comparator, setPosition);
} else {
throw new Error(`Unknown implementation: ${impl}`);
}
}

function benchmarkWebServer(queueSize, impl) {
// Typical web server: mostly peek(), occasional insert/shift
// Queue holds 3-5 TimersList objects
const comparator = (a, b) => a.expiry - b.expiry;
const setPosition = (node, pos) => { node.pos = pos; };
const heap = createHeap(impl, comparator, setPosition);

// Initial queue state
const timers = [];
for (let i = 0; i < queueSize; i++) {
timers.push({
expiry: 1000 + i * 10000,
id: i,
pos: null,
});
heap.insert(timers[i]);
}

const iterations = 100000;
bench.start();

for (let i = 0; i < iterations; i++) {
const op = Math.random();

if (op < 0.80) {
// 80% peek() - ULTRA HOT PATH in processTimers()
heap.peek();
} else if (op < 0.90) {
// 10% shift + insert (timer list expires, new one created)
heap.shift();
const newTimer = {
expiry: 1000 + Math.floor(Math.random() * 100000),
id: i,
pos: null,
};
heap.insert(newTimer);
} else {
// 10% percolateDown (timer list rescheduled)
const min = heap.peek();
if (min) {
min.expiry += 100;
heap.percolateDown(1);
}
}
}

bench.end(iterations);
}

function benchmarkMicroservice(queueSize, impl) {
// Microservice: balanced mix of operations
const comparator = (a, b) => a.expiry - b.expiry;
const setPosition = (node, pos) => { node.pos = pos; };
const heap = createHeap(impl, comparator, setPosition);

// Initial queue state
for (let i = 0; i < queueSize; i++) {
heap.insert({
expiry: 1000 + i * 5000,
id: i,
pos: null,
});
}

const iterations = 100000;
bench.start();

for (let i = 0; i < iterations; i++) {
const op = Math.random();

if (op < 0.70) {
// 70% peek()
heap.peek();
} else if (op < 0.85) {
// 15% insert
heap.insert({
expiry: 1000 + Math.floor(Math.random() * 60000),
id: i,
pos: null,
});
} else if (op < 0.95) {
// 10% shift
if (heap.size > 0) heap.shift();
} else {
// 5% percolateDown
const min = heap.peek();
if (min) {
min.expiry += 50;
heap.percolateDown(1);
}
}
}

bench.end(iterations);
}

function benchmarkRealTime(queueSize, impl) {
// Real-time app: frequent updates, many peek()
const comparator = (a, b) => a.expiry - b.expiry;
const setPosition = (node, pos) => { node.pos = pos; };
const heap = createHeap(impl, comparator, setPosition);

// Initial queue state
for (let i = 0; i < queueSize; i++) {
heap.insert({
expiry: 50 + i * 1000,
id: i,
pos: null,
});
}

const iterations = 100000;
bench.start();

for (let i = 0; i < iterations; i++) {
const op = Math.random();

if (op < 0.85) {
// 85% peek() - event loop checking next timer constantly
heap.peek();
} else if (op < 0.92) {
// 7% percolateDown (timer reschedule)
const min = heap.peek();
if (min) {
min.expiry += 10;
heap.percolateDown(1);
}
} else {
// 8% shift + insert
if (heap.size > 0) heap.shift();
heap.insert({
expiry: 50 + Math.floor(Math.random() * 10000),
id: i,
pos: null,
});
}
}

bench.end(iterations);
}

function main({ queueSize, impl, workload }) {
switch (workload) {
case 'web-server':
benchmarkWebServer(queueSize, impl);
break;
case 'microservice':
benchmarkMicroservice(queueSize, impl);
break;
case 'real-time':
benchmarkRealTime(queueSize, impl);
break;
default:
throw new Error(`Unknown workload: ${workload}`);
}
}
107 changes: 107 additions & 0 deletions benchmark/timers/priority-queue-statistical.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
'use strict';

// Statistical benchmark with variance/stddev reporting
// Runs multiple trials to ensure reproducibility

const common = require('../common.js');

const bench = common.createBenchmark(main, {
queueSize: [5, 10, 20, 100, 1000, 10000],
impl: ['original', 'adaptive'],
trials: [10],
});

function createHeap(impl, comparator, setPosition) {
if (impl === 'original') {
const PriorityQueue = require('../../lib/internal/priority_queue');
return new PriorityQueue(comparator, setPosition);
} else if (impl === 'adaptive') {
const PriorityQueueAdaptive = require('../../lib/internal/priority_queue_adaptive');
return new PriorityQueueAdaptive(comparator, setPosition);
} else {
throw new Error(`Unknown implementation: ${impl}`);
}
}

function runTrial(queueSize, impl) {
const comparator = (a, b) => a.expiry - b.expiry;
const setPosition = (node, pos) => { node.pos = pos; };
const heap = createHeap(impl, comparator, setPosition);

// Initial queue state
const timers = [];
for (let i = 0; i < queueSize; i++) {
timers.push({
expiry: 1000 + i * 10000,
id: i,
pos: null,
});
heap.insert(timers[i]);
}

const iterations = 100000;
const startTime = process.hrtime.bigint();

for (let i = 0; i < iterations; i++) {
const op = (i % 100) / 100; // Deterministic pattern

if (op < 0.80) {
// 80% peek()
heap.peek();
} else if (op < 0.90) {
// 10% shift + insert
heap.shift();
const newTimer = {
expiry: 1000 + ((i * 12345) % 100000), // Deterministic random
id: i,
pos: null,
};
heap.insert(newTimer);
} else {
// 10% percolateDown
const min = heap.peek();
if (min) {
min.expiry += 100;
heap.percolateDown(1);
}
}
}

const endTime = process.hrtime.bigint();
const durationNs = Number(endTime - startTime);
const opsPerSec = (iterations / durationNs) * 1e9;

return opsPerSec;
}

function mean(values) {
return values.reduce((a, b) => a + b, 0) / values.length;
}

function stddev(values) {
const avg = mean(values);
const squareDiffs = values.map((value) => Math.pow(value - avg, 2));
const avgSquareDiff = mean(squareDiffs);
return Math.sqrt(avgSquareDiff);
}

function main({ queueSize, impl, trials }) {
const results = [];

// Run multiple trials
for (let i = 0; i < trials; i++) {
results.push(runTrial(queueSize, impl));
}

const avg = mean(results);
const std = stddev(results);
const cv = (std / avg) * 100; // Coefficient of variation

bench.start();
// Report mean as the primary metric
bench.end(avg);

// Log statistical info (will appear in benchmark output)
console.error(`n=${queueSize} impl=${impl}: ${avg.toFixed(0)} ops/sec ` +
`(stddev=${std.toFixed(0)}, cv=${cv.toFixed(2)}%)`);
}
Loading