(() => {
"use strict";
const BROKERS = [
{ name: "NATS", repo: "ruststream-nats" },
{ name: "Redis", repo: "ruststream-fred" },
{ name: "RabbitMQ", repo: "ruststream-lapin" },
{ name: "Kafka", repo: "ruststream-rdkafka" },
{ name: "AMQP 1.0", repo: "ruststream-amqp" },
{ name: "Google Cloud Pub/Sub", repo: "ruststream-gcp-pubsub" },
{ name: "AWS SQS / SNS", repo: "ruststream-sqs-sns" },
{ name: "Apache Pulsar", repo: "ruststream-pulsar" },
{ name: "MQTT 5", repo: "ruststream-rumqttc" },
{ name: "ZeroMQ", repo: "ruststream-zeromq" },
{ name: "Stream files / stdio", repo: "ruststream-sea-file" },
{ name: "AWS Kinesis", repo: "ruststream-kinesis" },
];
const CORE = { name: "RustStream", repo: "ruststream" };
const SITE = "https://powersemmi.github.io/";
const RESULTS = "/latest/benchmarks/results.json";
const PAGE = "/latest/benchmarks/";
const SCHEMAS = [1, 2, 3];
const TIMEOUT_MS = 8000;
const EMPTY = "-";
const text = (tag, value) => {
const node = document.createElement(tag);
node.textContent = value;
return node;
};
const link = (href, label) => {
const node = text("a", label);
node.href = href;
return node;
};
async function load(broker) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
try {
const response = await fetch(SITE + broker.repo + RESULTS, { signal: controller.signal });
if (!response.ok) {
return null;
}
const results = await response.json();
return results && SCHEMAS.includes(results.schema) ? results : null;
} catch {
return null;
} finally {
clearTimeout(timer);
}
}
const number = (value, lang) =>
typeof value === "number" ? value.toLocaleString(lang, { maximumFractionDigits: 1 }) : "-";
function side(measurement, unit, lang) {
if (!measurement) {
return "-";
}
if (typeof measurement.best === "number") {
const best = number(measurement.best, lang) + " " + unit;
const typical =
typeof measurement.median === "number" ? measurement.median : measurement.worst;
if (typeof typical !== "number") {
return best;
}
return best + " (" + number(typical, lang) + ")";
}
const median = number(measurement.median, lang) + " " + unit;
if (typeof measurement.min !== "number" || typeof measurement.max !== "number") {
return median;
}
return median + " (" + number(measurement.min, lang) + "-" + number(measurement.max, lang) + ")";
}
function overhead(scenario, labels) {
let value =
scenario.verdict === "indistinguishable"
? labels.indistinguishable
: (scenario.overhead_percent >= 0 ? "+" : "") + scenario.overhead_percent + "%";
if (scenario.broker_bound) {
value += " (" + labels.brokerBound + ")";
}
return value;
}
const figure = (measurement) =>
typeof measurement?.best === "number" ? measurement.best : measurement?.median;
function spread(measurement) {
if (typeof measurement?.best === "number" && typeof measurement?.worst === "number") {
return measurement.best - measurement.worst;
}
if (!measurement || typeof measurement.min !== "number" || typeof measurement.max !== "number") {
return null;
}
return measurement.max - measurement.min;
}
function indistinguishable(scenario) {
if (scenario.adapter_verdict) {
return false;
}
const ours = spread(scenario.adapter);
const theirs = spread(scenario.raw);
if (ours === null || theirs === null) {
return false;
}
return Math.abs(figure(scenario.raw) - figure(scenario.adapter)) < Math.max(ours, theirs);
}
function adapter(scenario, lang, labels) {
if (!scenario.adapter) {
return EMPTY;
}
let value = side(scenario.adapter, scenario.unit, lang);
if (scenario.adapter_verdict === "indistinguishable" || indistinguishable(scenario)) {
value += " " + labels.against.replace("{percent}", labels.indistinguishable);
} else if (typeof scenario.adapter_overhead_percent === "number") {
const percent = scenario.adapter_overhead_percent;
value += " " + labels.against.replace("{percent}", (percent >= 0 ? "+" : "") + percent + "%");
}
return value;
}
function table(published, labels, lang) {
const element = document.createElement("table");
const head = element.createTHead().insertRow();
for (const column of [
labels.broker,
labels.scenario,
labels.raw,
labels.adapter,
labels.framework,
labels.overhead,
]) {
head.appendChild(text("th", column));
}
const body = element.createTBody();
for (const { broker, results } of published) {
for (const [index, scenario] of results.scenarios.entries()) {
const row = body.insertRow();
row.appendChild(text("td", index === 0 ? broker.name : ""));
row.appendChild(text("td", scenario.name));
row.appendChild(text("td", side(scenario.raw, scenario.unit, lang)));
row.appendChild(text("td", adapter(scenario, lang, labels)));
row.appendChild(text("td", side(scenario.framework, scenario.unit, lang)));
row.appendChild(text("td", overhead(scenario, labels)));
}
}
return element;
}
function code(published, labels, lang) {
const element = document.createElement("table");
const head = element.createTHead().insertRow();
const columns = [
labels.crate,
labels.scenario,
labels.instructions,
labels.allocations,
labels.cold,
];
for (const column of columns) {
head.appendChild(text("th", column));
}
const body = element.createTBody();
for (const { broker, results } of published) {
for (const [index, scenario] of results.code.entries()) {
const row = body.insertRow();
row.appendChild(text("td", index === 0 ? broker.name : ""));
row.appendChild(text("td", scenario.name));
row.appendChild(text("td", number(scenario.framework.instructions, lang)));
row.appendChild(text("td", number(scenario.framework.allocations, lang)));
row.appendChild(
text(
"td",
scenario.cold
? number(scenario.cold.instructions, lang) + " / " + number(scenario.cold.allocations, lang)
: "-",
),
);
}
}
return element;
}
function provenance(published, labels) {
const list = document.createElement("ul");
for (const { broker, results } of published) {
const environment = results.environment || {};
const parts = [
results.crate + " " + results.crate_version + " on ruststream " + results.core_version,
environment.cpu,
environment.os,
environment.broker,
labels.measured + " " + results.measured_at,
].filter(Boolean);
const item = document.createElement("li");
item.appendChild(text("strong", broker.name));
item.appendChild(document.createTextNode(" - " + parts.join(", ") + ". "));
item.appendChild(link(SITE + broker.repo + PAGE, labels.details));
list.appendChild(item);
}
return list;
}
const MACHINE = [
"cpu",
"architecture",
"cpu_frequency",
"cores",
"memory",
"memory_speed",
"os",
"rustc",
"valgrind",
"profile",
"features",
];
function machine(published, labels) {
const list = document.createElement("ul");
for (const { broker, results } of published) {
const environment = results.environment || {};
const parts = MACHINE.map((field) => environment[field]).filter(Boolean);
const item = document.createElement("li");
item.appendChild(text("strong", broker.name));
item.appendChild(
document.createTextNode(
" - " + parts.join(", ") + ". " + labels.measured + " " + results.measured_at + ".",
),
);
list.appendChild(item);
}
return list;
}
function renderCode(container, labels, lang, loaded) {
const published = loaded.filter((entry) => entry.results && entry.results.code?.length);
container.replaceChildren();
if (published.length) {
container.appendChild(code(published, labels, lang));
container.appendChild(machine(published, labels));
} else {
container.appendChild(text("p", labels.pending.replace("{brokers}", CORE.name)));
}
}
function render(container, labels, lang, loaded) {
const published = loaded.filter((entry) => entry.results && entry.results.scenarios?.length);
const pending = loaded.filter((entry) => !published.includes(entry));
container.replaceChildren();
if (published.length) {
container.appendChild(table(published, labels, lang));
container.appendChild(provenance(published, labels));
}
if (pending.length) {
const names = pending.map((entry) => entry.broker.name).join(", ");
container.appendChild(text("p", labels.pending.replace("{brokers}", names)));
}
}
async function main() {
const container = document.getElementById("benchmark-results");
const codeContainer = document.getElementById("benchmark-code");
if (!container && !codeContainer) {
return;
}
const lang = document.documentElement.lang || "en";
const source = container?.dataset.benchmarkLabels ? container : codeContainer;
const labels = JSON.parse(source.dataset.benchmarkLabels);
for (const element of [container, codeContainer]) {
element?.replaceChildren(text("p", labels.loading));
}
const loaded = await Promise.all(
[...BROKERS, CORE].map(async (broker) => ({ broker, results: await load(broker) })),
);
const brokers = loaded.filter((entry) => entry.broker !== CORE);
if (container) {
render(container, labels, lang, brokers);
}
if (codeContainer) {
const core = loaded.filter((entry) => entry.broker === CORE);
renderCode(codeContainer, labels, lang, [...core, ...brokers]);
}
}
if (window.document$) {
window.document$.subscribe(main);
} else {
document.addEventListener("DOMContentLoaded", main);
}
})();