Skip to content
Merged
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"test:autobahn": "cd test/autobahn && ./run-wstest.js",
"bench": "vitest bench --run --config vitest.bench.config.mjs",
"bench:baseline": "node test/benchmark/track-performance.mjs save",
"bench:compare": "node test/benchmark/track-performance.mjs compare",
"bench:check": "node test/benchmark/track-performance.mjs check",
"lint": "eslint lib/**/*.js test/**/*.js",
"lint:fix": "eslint lib/**/*.js test/**/*.js --fix"
Expand Down
30 changes: 14 additions & 16 deletions test/benchmark/baseline.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
{
"timestamp": "2025-10-06T16:43:17.731Z",
"timestamp": "2025-10-06T17:27:59.641Z",
"results": {
"WebSocketConnection Performance 3866ms": {
"create connection instance": 28323.39,
"send small UTF-8 message": 28201.35,
"send medium UTF-8 message (1KB)": 24615.97,
"send binary message (1KB)": 24889.31,
"send ping frame": 31562.37,
"send pong frame": 32393.72
"WebSocketConnection Performance 7359ms": {
"create connection instance": 28847.63,
"send small UTF-8 message": 914160.4,
"send medium UTF-8 message (1KB)": 108086.21,
"send binary message (1KB)": 222918.81,
"send ping frame": 2375454.67,
"send pong frame": 1935975.19
},
"WebSocketFrame Performance 10001ms": {
"serialize small text frame (17 bytes, unmasked)": 4427042.23,
"serialize small text frame (17 bytes, masked)": 3005215.87,
"serialize medium binary frame (1KB)": 4239270.36,
"serialize large binary frame (64KB)": 4024552.34
},
"WebSocketConnection Performance": {},
"WebSocketFrame Performance": {}
"WebSocketFrame Performance 9745ms": {
"serialize small text frame (17 bytes, unmasked)": 4240401.67,
"serialize small text frame (17 bytes, masked)": 3070532.44,
"serialize medium binary frame (1KB)": 4418217.61,
"serialize large binary frame (64KB)": 3918678.38
}
}
}
45 changes: 18 additions & 27 deletions test/benchmark/connection-operations.bench.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,51 +3,42 @@ import WebSocketConnection from '../../lib/WebSocketConnection.js';
import { MockSocket } from '../helpers/mocks.mjs';

describe('WebSocketConnection Performance', () => {
// Pre-allocate messages and buffers outside benchmarks
const smallMessage = 'Hello, WebSocket!';
const mediumMessage = 'x'.repeat(1024);
const binaryBuffer = Buffer.alloc(1024);

// Shared connection for send operations (created once, reused across all iterations)
// Note: We initialize this directly rather than using beforeAll() because Vitest's
// benchmark runner doesn't execute hooks before benchmarks in the same way as test()
const sharedSocket = new MockSocket();
const sharedConnection = new WebSocketConnection(sharedSocket, [], 'echo-protocol', false, {});
sharedConnection._addSocketEventListeners();
sharedConnection.state = 'open';

bench('create connection instance', () => {
const socket = new MockSocket();
const connection = new WebSocketConnection(socket, [], 'echo-protocol', false, {});
connection._addSocketEventListeners();
});

bench('send small UTF-8 message', () => {
const socket = new MockSocket();
const connection = new WebSocketConnection(socket, [], 'echo-protocol', false, {});
connection._addSocketEventListeners();
connection.state = 'open';
connection.sendUTF('Hello, WebSocket!');
sharedConnection.sendUTF(smallMessage);
});

bench('send medium UTF-8 message (1KB)', () => {
const socket = new MockSocket();
const connection = new WebSocketConnection(socket, [], 'echo-protocol', false, {});
connection._addSocketEventListeners();
connection.state = 'open';
const message = 'x'.repeat(1024);
connection.sendUTF(message);
sharedConnection.sendUTF(mediumMessage);
});

bench('send binary message (1KB)', () => {
const socket = new MockSocket();
const connection = new WebSocketConnection(socket, [], 'echo-protocol', false, {});
connection._addSocketEventListeners();
connection.state = 'open';
const buffer = Buffer.alloc(1024);
connection.sendBytes(buffer);
sharedConnection.sendBytes(binaryBuffer);
});

bench('send ping frame', () => {
const socket = new MockSocket();
const connection = new WebSocketConnection(socket, [], 'echo-protocol', false, {});
connection._addSocketEventListeners();
connection.state = 'open';
connection.ping();
sharedConnection.ping();
});

bench('send pong frame', () => {
const socket = new MockSocket();
const connection = new WebSocketConnection(socket, [], 'echo-protocol', false, {});
connection._addSocketEventListeners();
connection.state = 'open';
connection.pong();
sharedConnection.pong();
});
});
6 changes: 5 additions & 1 deletion test/benchmark/track-performance.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ function parseBenchmarkOutput(output) {
// Detect suite name
if (line.includes('> WebSocket')) {
currentSuite = line.match(/> (.*)/)[1].trim();
results[currentSuite] = {};
// Don't initialize suite here - wait until first benchmark is found
}

// Parse benchmark results
const benchMatch = line.match(/^\s*[·•]\s+(.+?)\s+(\d+(?:,\d+)*(?:\.\d+)?)\s/);
if (benchMatch && currentSuite) {
const [, name, hz] = benchMatch;
// Lazily initialize suite only when first benchmark is found
if (!results[currentSuite]) {
results[currentSuite] = {};
}
results[currentSuite][name.trim()] = parseFloat(hz.replace(/,/g, ''));
}
}
Expand Down
1 change: 0 additions & 1 deletion vitest.bench.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { defineConfig } from 'vitest/config';

export default defineConfig({
test: {
include: ['test/benchmark/**/*.bench.mjs'],
benchmark: {
include: ['test/benchmark/**/*.bench.mjs'],
exclude: ['node_modules/', 'test/unit/', 'test/integration/'],
Expand Down