bench.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import algorithms from "./algorithms";
  2. const defaultDifficulty = 4;
  3. const status: HTMLParagraphElement = document.getElementById(
  4. "status",
  5. ) as HTMLParagraphElement;
  6. const difficultyInput: HTMLInputElement = document.getElementById(
  7. "difficulty-input",
  8. ) as HTMLInputElement;
  9. const algorithmSelect: HTMLSelectElement = document.getElementById(
  10. "algorithm-select",
  11. ) as HTMLSelectElement;
  12. const compareSelect: HTMLSelectElement = document.getElementById(
  13. "compare-select",
  14. ) as HTMLSelectElement;
  15. const header: HTMLTableRowElement = document.getElementById(
  16. "table-header",
  17. ) as HTMLTableRowElement;
  18. const headerCompare: HTMLTableSectionElement = document.getElementById(
  19. "table-header-compare",
  20. ) as HTMLTableSectionElement;
  21. const results: HTMLTableRowElement = document.getElementById(
  22. "results",
  23. ) as HTMLTableRowElement;
  24. const setupControls = () => {
  25. if (defaultDifficulty == null) {
  26. return;
  27. }
  28. difficultyInput.value = defaultDifficulty.toString();
  29. for (const alg of Object.keys(algorithms)) {
  30. const option1 = document.createElement("option");
  31. algorithmSelect?.append(option1);
  32. const option2 = document.createElement("option");
  33. compareSelect.append(option2);
  34. option1.value = option1.innerText = option2.value = option2.innerText = alg;
  35. }
  36. };
  37. const benchmarkTrial = async (stats, difficulty, algorithm, signal) => {
  38. if (!(difficulty >= 1)) {
  39. throw new Error(`Invalid difficulty: ${difficulty}`);
  40. }
  41. const process = algorithms[algorithm];
  42. if (process == null) {
  43. throw new Error(`Unknown algorithm: ${algorithm}`);
  44. }
  45. const rawChallenge = new Uint8Array(32);
  46. crypto.getRandomValues(rawChallenge);
  47. const challenge = Array.from(rawChallenge)
  48. .map((c) => c.toString(16).padStart(2, "0"))
  49. .join("");
  50. const t0 = performance.now();
  51. const { hash, nonce } = await process(
  52. { basePrefix: "/", version: "devel" },
  53. challenge,
  54. Number(difficulty),
  55. signal,
  56. );
  57. const t1 = performance.now();
  58. console.log({ hash, nonce });
  59. stats.time += t1 - t0;
  60. stats.iters += nonce;
  61. return { time: t1 - t0, nonce };
  62. };
  63. const stats = { time: 0, iters: 0 };
  64. const comparison = { time: 0, iters: 0 };
  65. const updateStatus = () => {
  66. const mainRate = stats.iters / stats.time;
  67. const compareRate = comparison.iters / comparison.time;
  68. if (Number.isFinite(mainRate)) {
  69. status.innerText = `Average hashrate: ${mainRate.toFixed(3)}kH/s`;
  70. if (Number.isFinite(compareRate)) {
  71. const change = ((mainRate - compareRate) / mainRate) * 100;
  72. status.innerText += ` vs ${compareRate.toFixed(3)}kH/s (${change.toFixed(2)}% change)`;
  73. }
  74. } else {
  75. status.innerText = "Benchmarking...";
  76. }
  77. };
  78. const tableCell = (text) => {
  79. const td = document.createElement("td");
  80. td.innerText = text;
  81. td.style.padding = "0 0.25rem";
  82. return td;
  83. };
  84. const benchmarkLoop = async (controller) => {
  85. const difficulty = difficultyInput.value;
  86. const algorithm = algorithmSelect.value;
  87. const compareAlgorithm = compareSelect.value;
  88. updateStatus();
  89. try {
  90. const { time, nonce } = await benchmarkTrial(
  91. stats,
  92. difficulty,
  93. algorithm,
  94. controller.signal,
  95. );
  96. const tr = document.createElement("tr");
  97. tr.style.display = "contents";
  98. tr.append(tableCell(`${time}ms`), tableCell(nonce));
  99. // auto-scroll to new rows
  100. const atBottom =
  101. results.scrollHeight - results.clientHeight <= results.scrollTop;
  102. results.append(tr);
  103. if (atBottom) {
  104. results.scrollTop = results.scrollHeight - results.clientHeight;
  105. }
  106. updateStatus();
  107. if (compareAlgorithm !== "NONE") {
  108. const { time, nonce } = await benchmarkTrial(
  109. comparison,
  110. difficulty,
  111. compareAlgorithm,
  112. controller.signal,
  113. );
  114. tr.append(tableCell(`${time}ms`), tableCell(nonce));
  115. }
  116. } catch (e) {
  117. if (e !== false) {
  118. status.innerText = e;
  119. }
  120. return;
  121. }
  122. await benchmarkLoop(controller);
  123. };
  124. let controller: AbortController | null = null;
  125. const reset = () => {
  126. stats.time = stats.iters = 0;
  127. comparison.time = comparison.iters = 0;
  128. results.innerHTML = status.innerText = "";
  129. const table = results.parentElement as HTMLElement;
  130. if (compareSelect.value !== "NONE") {
  131. table.style.gridTemplateColumns = "repeat(4,auto)";
  132. header.style.display = "none";
  133. headerCompare.style.display = "contents";
  134. } else {
  135. table.style.gridTemplateColumns = "repeat(2,auto)";
  136. header.style.display = "contents";
  137. headerCompare.style.display = "none";
  138. }
  139. if (controller != null) {
  140. controller.abort();
  141. }
  142. controller = new AbortController();
  143. void benchmarkLoop(controller);
  144. };
  145. setupControls();
  146. difficultyInput.addEventListener("change", reset);
  147. algorithmSelect.addEventListener("change", reset);
  148. compareSelect.addEventListener("change", reset);
  149. reset();