aboutsummaryrefslogtreecommitdiff
path: root/web/js/bench.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'web/js/bench.mjs')
-rw-r--r--web/js/bench.mjs152
1 files changed, 152 insertions, 0 deletions
diff --git a/web/js/bench.mjs b/web/js/bench.mjs
new file mode 100644
index 0000000..c8c69bd
--- /dev/null
+++ b/web/js/bench.mjs
@@ -0,0 +1,152 @@
+import processFast from "./proof-of-work.mjs";
+import processSlow from "./proof-of-work-slow.mjs";
+
+const defaultDifficulty = 4;
+const algorithms = {
+ fast: processFast,
+ slow: processSlow,
+};
+
+const status = document.getElementById("status");
+const difficultyInput = document.getElementById("difficulty-input");
+const algorithmSelect = document.getElementById("algorithm-select");
+const compareSelect = document.getElementById("compare-select");
+const header = document.getElementById("table-header");
+const headerCompare = document.getElementById("table-header-compare");
+const results = document.getElementById("results");
+
+const setupControls = () => {
+ difficultyInput.value = defaultDifficulty;
+ for (const alg of Object.keys(algorithms)) {
+ const option1 = document.createElement("option");
+ algorithmSelect.append(option1);
+ const option2 = document.createElement("option");
+ compareSelect.append(option2);
+ option1.value = option1.innerText = option2.value = option2.innerText = alg;
+ }
+};
+
+const benchmarkTrial = async (stats, difficulty, algorithm, signal) => {
+ if (!(difficulty >= 1)) {
+ throw new Error(`Invalid difficulty: ${difficulty}`);
+ }
+ const process = algorithms[algorithm];
+ if (process == null) {
+ throw new Error(`Unknown algorithm: ${algorithm}`);
+ }
+
+ const rawChallenge = new Uint8Array(32);
+ crypto.getRandomValues(rawChallenge);
+ const challenge = Array.from(rawChallenge)
+ .map((c) => c.toString(16).padStart(2, "0"))
+ .join("");
+
+ const t0 = performance.now();
+ const { hash, nonce } = await process(challenge, Number(difficulty), signal);
+ const t1 = performance.now();
+ console.log({ hash, nonce });
+
+ stats.time += t1 - t0;
+ stats.iters += nonce;
+
+ return { time: t1 - t0, nonce };
+};
+
+const stats = { time: 0, iters: 0 };
+const comparison = { time: 0, iters: 0 };
+const updateStatus = () => {
+ const mainRate = stats.iters / stats.time;
+ const compareRate = comparison.iters / comparison.time;
+ if (Number.isFinite(mainRate)) {
+ status.innerText = `Average hashrate: ${mainRate.toFixed(3)}kH/s`;
+ if (Number.isFinite(compareRate)) {
+ const change = ((mainRate - compareRate) / mainRate) * 100;
+ status.innerText += ` vs ${compareRate.toFixed(3)}kH/s (${change.toFixed(2)}% change)`;
+ }
+ } else {
+ status.innerText = "Benchmarking...";
+ }
+};
+
+const tableCell = (text) => {
+ const td = document.createElement("td");
+ td.innerText = text;
+ td.style.padding = "0 0.25rem";
+ return td;
+};
+
+const benchmarkLoop = async (controller) => {
+ const difficulty = difficultyInput.value;
+ const algorithm = algorithmSelect.value;
+ const compareAlgorithm = compareSelect.value;
+ updateStatus();
+
+ try {
+ const { time, nonce } = await benchmarkTrial(
+ stats,
+ difficulty,
+ algorithm,
+ controller.signal,
+ );
+
+ const tr = document.createElement("tr");
+ tr.style.display = "contents";
+ tr.append(tableCell(`${time}ms`), tableCell(nonce));
+
+ // auto-scroll to new rows
+ const atBottom =
+ results.scrollHeight - results.clientHeight <= results.scrollTop;
+ results.append(tr);
+ if (atBottom) {
+ results.scrollTop = results.scrollHeight - results.clientHeight;
+ }
+ updateStatus();
+
+ if (compareAlgorithm !== "NONE") {
+ const { time, nonce } = await benchmarkTrial(
+ comparison,
+ difficulty,
+ compareAlgorithm,
+ controller.signal,
+ );
+ tr.append(tableCell(`${time}ms`), tableCell(nonce));
+ }
+ } catch (e) {
+ if (e !== false) {
+ status.innerText = e;
+ }
+ return;
+ }
+
+ benchmarkLoop(controller);
+};
+
+let controller = null;
+const reset = () => {
+ stats.time = stats.iters = 0;
+ comparison.time = comparison.iters = 0;
+ results.innerHTML = status.innerText = "";
+
+ const table = results.parentElement;
+ if (compareSelect.value !== "NONE") {
+ table.style.gridTemplateColumns = "repeat(4,auto)";
+ header.style.display = "none";
+ headerCompare.style.display = "contents";
+ } else {
+ table.style.gridTemplateColumns = "repeat(2,auto)";
+ header.style.display = "contents";
+ headerCompare.style.display = "none";
+ }
+
+ if (controller != null) {
+ controller.abort();
+ }
+ controller = new AbortController();
+ benchmarkLoop(controller);
+};
+
+setupControls();
+difficultyInput.addEventListener("change", reset);
+algorithmSelect.addEventListener("change", reset);
+compareSelect.addEventListener("change", reset);
+reset(); \ No newline at end of file