Skip to content

Instantly share code, notes, and snippets.

@jpcamara
Last active October 26, 2024 01:18
Show Gist options
  • Save jpcamara/8a1a09c9c67347c4e32384b9ce806b70 to your computer and use it in GitHub Desktop.
Save jpcamara/8a1a09c9c67347c4e32384b9ce806b70 to your computer and use it in GitHub Desktop.
Run with ruby-3.4.0-preview2
- gem install falcon
- RUBY_YJIT_ENABLE=1 falcon serve --bind http://localhost:4001 --count 1 -c chat_server.ru
- bun run chat-client.mjs
Connecting 32 WebSocket clients...
[186.53ms] All 32 clients connected
230567 messages per second (32 clients x 1600 msg, min delay: 64ms)
239922 messages per second (32 clients x 1600 msg, min delay: 64ms)
271556 messages per second (32 clients x 1600 msg, min delay: 64ms)
242132 messages per second (32 clients x 1600 msg, min delay: 64ms)
240036 messages per second (32 clients x 1600 msg, min delay: 64ms)
275979 messages per second (32 clients x 1600 msg, min delay: 64ms)
224858 messages per second (32 clients x 1600 msg, min delay: 64ms)
278455 messages per second (32 clients x 1600 msg, min delay: 64ms)
252987 messages per second (32 clients x 1600 msg, min delay: 64ms)
226450 messages per second (32 clients x 1600 msg, min delay: 64ms)
Run w/ node v21.7.3
- npm install g ws
- node chat-server.node.mjs
- bun run chat-client.mjs
Connecting 32 WebSocket clients...
[116.39ms] All 32 clients connected
273952 messages per second (32 clients x 1600 msg, min delay: 64ms)
322702 messages per second (32 clients x 1600 msg, min delay: 64ms)
297537 messages per second (32 clients x 1600 msg, min delay: 64ms)
318811 messages per second (32 clients x 1600 msg, min delay: 64ms)
298009 messages per second (32 clients x 1600 msg, min delay: 64ms)
263463 messages per second (32 clients x 1600 msg, min delay: 64ms)
315950 messages per second (32 clients x 1600 msg, min delay: 64ms)
301584 messages per second (32 clients x 1600 msg, min delay: 64ms)
288615 messages per second (32 clients x 1600 msg, min delay: 64ms)
256987 messages per second (32 clients x 1600 msg, min delay: 64ms)
Run w/ bun 1.1.33
- source here: https://github.com/oven-sh/bun/blob/main/bench/websocket-server/chat-server.bun.js
- bun run chat-server.bun.js
Connecting 32 WebSocket clients...
[107.64ms] All 32 clients connected
498780 messages per second (32 clients x 1600 msg, min delay: 64ms)
943795 messages per second (32 clients x 1600 msg, min delay: 64ms)
635219 messages per second (32 clients x 1600 msg, min delay: 64ms)
728121 messages per second (32 clients x 1600 msg, min delay: 64ms)
876407 messages per second (32 clients x 1600 msg, min delay: 64ms)
695959 messages per second (32 clients x 1600 msg, min delay: 64ms)
886135 messages per second (32 clients x 1600 msg, min delay: 64ms)
667740 messages per second (32 clients x 1600 msg, min delay: 64ms)
913562 messages per second (32 clients x 1600 msg, min delay: 64ms)
590218 messages per second (32 clients x 1600 msg, min delay: 64ms)
// Taken from https://github.com/oven-sh/bun/blob/main/bench/websocket-server/chat-client.mjs
// Run using:
// bun run chat-client.mjs
const env = "process" in globalThis ? process.env : "Deno" in globalThis ? Deno.env.toObject() : {};
const SERVER = env.SERVER || "ws://0.0.0.0:4001";
const WebSocket = globalThis.WebSocket || (await import("ws")).WebSocket;
const LOG_MESSAGES = env.LOG_MESSAGES === "1";
const CLIENTS_TO_WAIT_FOR = parseInt(env.CLIENTS_COUNT || "", 10) || 32;
const DELAY = 64;
const MESSAGES_TO_SEND = Array.from({ length: 32 }, () => [
"Hello World!",
"Hello World! 1",
"Hello World! 2",
"Hello World! 3",
"Hello World! 4",
"Hello World! 5",
"Hello World! 6",
"Hello World! 7",
"Hello World! 8",
"Hello World! 9",
"What is the meaning of life?",
"where is the bathroom?",
"zoo",
"kangaroo",
"erlang",
"elixir",
"bun",
"mochi",
"typescript",
"javascript",
"Hello World! 7",
"Hello World! 8",
"Hello World! 9",
"What is the meaning of life?",
"where is the bathroom?",
"zoo",
"kangaroo",
"erlang",
"elixir",
"bun",
"mochi",
"typescript",
"javascript",
"Hello World! 7",
"Hello World! 8",
"Hello World! 9",
"What is the meaning of life?",
"Hello World! 7",
"Hello World! 8",
"Hello World! 9",
"What is the meaning of life?",
"where is the bathroom?",
"zoo",
"kangaroo",
"erlang",
"elixir",
"bun",
"mochi",
"typescript",
"javascript",
]).flat();
const NAMES = Array.from({ length: 50 }, (a, i) => [
"Alice" + i,
"Bob" + i,
"Charlie" + i,
"David" + i,
"Eve" + i,
"Frank" + i,
"Grace" + i,
"Heidi" + i,
"Ivan" + i,
"Judy" + i,
"Karl" + i,
"Linda" + i,
"Mike" + i,
"Nancy" + i,
"Oscar" + i,
"Peggy" + i,
"Quentin" + i,
"Ruth" + i,
"Steve" + i,
"Trudy" + i,
"Ursula" + i,
"Victor" + i,
"Wendy" + i,
"Xavier" + i,
"Yvonne" + i,
"Zach" + i,
])
.flat()
.slice(0, CLIENTS_TO_WAIT_FOR);
console.log(`Connecting ${CLIENTS_TO_WAIT_FOR} WebSocket clients...`);
console.time(`All ${CLIENTS_TO_WAIT_FOR} clients connected`);
var remainingClients = CLIENTS_TO_WAIT_FOR;
var promises = [];
const clients = new Array(CLIENTS_TO_WAIT_FOR);
for (let i = 0; i < CLIENTS_TO_WAIT_FOR; i++) {
clients[i] = new WebSocket(`${SERVER}?name=${NAMES[i]}`);
promises.push(
new Promise((resolve, reject) => {
clients[i].onmessage = event => {
resolve();
};
}),
);
}
await Promise.all(promises);
console.timeEnd(`All ${clients.length} clients connected`);
var received = 0;
var total = 0;
var more = false;
var remaining;
for (let i = 0; i < CLIENTS_TO_WAIT_FOR; i++) {
clients[i].onmessage = event => {
if (LOG_MESSAGES) console.log(event.data);
received++;
remaining--;
if (remaining === 0) {
more = true;
remaining = total;
}
};
}
// each message is supposed to be received
// by each client
// so its an extra loop
for (let i = 0; i < CLIENTS_TO_WAIT_FOR; i++) {
for (let j = 0; j < MESSAGES_TO_SEND.length; j++) {
for (let k = 0; k < CLIENTS_TO_WAIT_FOR; k++) {
total++;
}
}
}
remaining = total;
function restart() {
for (let i = 0; i < CLIENTS_TO_WAIT_FOR; i++) {
for (let j = 0; j < MESSAGES_TO_SEND.length; j++) {
clients[i].send(MESSAGES_TO_SEND[j]);
}
}
}
var runs = [];
setInterval(() => {
const last = received;
runs.push(last);
received = 0;
console.log(
last,
`messages per second (${CLIENTS_TO_WAIT_FOR} clients x ${MESSAGES_TO_SEND.length} msg, min delay: ${DELAY}ms)`,
);
if (runs.length >= 10) {
console.log("10 runs");
console.log(JSON.stringify(runs, null, 2));
if ("process" in globalThis) process.exit(0);
runs.length = 0;
}
}, 1000);
var isRestarting = false;
setInterval(() => {
if (more && !isRestarting) {
more = false;
isRestarting = true;
restart();
isRestarting = false;
}
}, DELAY);
restart();
// Taken from https://github.com/oven-sh/bun/blob/main/bench/websocket-server/chat-server.node.mjs
// Run using:
// node chat-server.node.mjs
const port = process.env.PORT || 4001;
const CLIENTS_TO_WAIT_FOR = parseInt(process.env.CLIENTS_COUNT || "", 10) || 32;
import { createRequire } from "module";
const require = createRequire(import.meta.url);
var WebSocketServer = require("ws").Server,
config = {
host: "0.0.0.0",
port,
},
wss = new WebSocketServer(config, function () {
console.log(`Waiting for ${CLIENTS_TO_WAIT_FOR} clients to connect..`);
});
var clients = [];
wss.on("connection", function (ws, { url }) {
const name = new URL(new URL(url, "http://localhost:3000")).searchParams.get("name");
console.log(`${name} connected (${CLIENTS_TO_WAIT_FOR - clients.length} remain)`);
clients.push(ws);
ws.on("message", function (message) {
const out = `${name}: ${message}`;
for (let client of clients) {
client.send(out);
}
});
// when a connection is closed
ws.on("close", function (ws) {
clients.splice(clients.indexOf(ws), 1);
});
if (clients.length === CLIENTS_TO_WAIT_FOR) {
sendReadyMessage();
}
});
function sendReadyMessage() {
console.log("All clients connected");
setTimeout(() => {
console.log("Starting benchmark");
for (let client of clients) {
client.send(`ready`);
}
}, 100);
}
# frozen_string_literal: true
# An async-websocket port of the bun.sh example websocket benchmark server here: https://github.com/oven-sh/bun/blob/main/bench/websocket-server/chat-server.bun.js
# Run using:
# RUBY_YJIT_ENABLE=1 falcon serve --bind http://localhost:4001 --count 1 -c chat_server.ru
require 'async/websocket/adapters/rack'
require 'set'
CLIENTS_TO_WAIT_FOR = (ENV['CLIENTS_COUNT'] || '32').to_i
$remaining_clients = CLIENTS_TO_WAIT_FOR
$connections = Set.new
$client_names = {}
run lambda { |env|
Async::WebSocket::Adapters::Rack.open(env, protocols: ['ws']) do |connection|
name = Rack::Request.new(env).params['name'] || "Client ##{CLIENTS_TO_WAIT_FOR - $remaining_clients}"
$client_names[connection.object_id] = name
$connections << connection
$remaining_clients -= 1
puts "#{name} connected (#{$remaining_clients} remain)"
if $remaining_clients == 0
puts "All clients connected"
Async do
sleep 0.1
puts 'Starting benchmark by sending "ready" message'
$connections.each do |conn|
conn.write('ready')
conn.flush
end
end
end
while message = connection.read
out = "#{$client_names[connection.object_id]}: #{message}"
$connections.each do |conn|
conn.write(out)
conn.flush
end
end
ensure
$connections.delete(connection)
$client_names.delete(connection.object_id)
$remaining_clients += 1
end
} or [200, {}, ["Error"]]
puts "Waiting for #{$remaining_clients} clients to connect...\n http://localhost:4001/"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment