Last active
October 26, 2024 01:18
-
-
Save jpcamara/8a1a09c9c67347c4e32384b9ce806b70 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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