web/ : le moteur tourne dans un Web Worker (module ES) ; la page de test donne le meilleur coup d'une position et son temps de calcul ; la page de mesure chronomètre les positions de référence, mesure la taille téléchargée et la mémoire, compare aux seuils de l'étape 0b et produit un rapport à copier. make bench fait la même mesure sous Node, make deploy-web sert les pages sur le réseau local. Guide de test pour le Fairphone 4 dans docs/guide-test-etape-0.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
98 lines
3.7 KiB
JavaScript
98 lines
3.7 KiB
JavaScript
// bench-page.mjs -- page de mesure (bench.html) : chronomètre le moteur
|
|
// dans un Web Worker et produit le rapport à copier.
|
|
//
|
|
// Copyright (C) 2026 les auteurs d'Ustagammon.
|
|
// Licence : GPLv3 ou ultérieure (voir LICENSE).
|
|
|
|
import { DOWNLOADED_FILES, DOWNLOAD_LIMIT_BYTES, formatReport, measureTimes, summarize }
|
|
from "./benchmark.mjs";
|
|
import { startEngine } from "./engine-client.mjs";
|
|
|
|
const $ = (id) => document.getElementById(id);
|
|
|
|
// Taille d'un fichier du site, brute et compressée gzip (comme le ferait un
|
|
// serveur qui compresse ses réponses).
|
|
async function fileSize(name) {
|
|
const blob = await (await fetch(name, { cache: "no-store" })).blob();
|
|
const gzipped = blob.stream().pipeThrough(new CompressionStream("gzip"));
|
|
const gzipBytes = (await new Response(gzipped).arrayBuffer()).byteLength;
|
|
return { name, bytes: blob.size, gzipBytes };
|
|
}
|
|
|
|
function addRow(cells) {
|
|
const row = $("table").insertRow();
|
|
for (const text of cells) {
|
|
row.insertCell().textContent = text;
|
|
}
|
|
}
|
|
|
|
async function measure() {
|
|
$("start").disabled = true;
|
|
$("results").hidden = true;
|
|
$("table").replaceChildren();
|
|
|
|
$("status").textContent = "Chargement du moteur…";
|
|
const start = performance.now();
|
|
const engine = startEngine();
|
|
await engine.ready;
|
|
const loadMs = performance.now() - start;
|
|
|
|
const { positions } = await (await fetch("wasm/positions.json")).json();
|
|
let memoryBytes = 0;
|
|
const times = await measureTimes({
|
|
bestMove: (...args) => engine.bestMove(...args).then(keepMemory),
|
|
cubeDecision: (...args) => engine.cubeDecision(...args).then(keepMemory),
|
|
}, positions, (done, total) => {
|
|
$("status").textContent = "Calcul " + done + " sur " + total + "…";
|
|
});
|
|
function keepMemory(answer) {
|
|
memoryBytes = answer.memoryBytes;
|
|
return answer;
|
|
}
|
|
|
|
$("status").textContent = "Mesure de la taille à télécharger…";
|
|
const files = await Promise.all(DOWNLOADED_FILES.map(fileSize));
|
|
|
|
for (const s of summarize(times)) {
|
|
addRow([s.label, Math.round(s.medianMs) + " ms", Math.round(s.maxMs) + " ms",
|
|
s.limitMs + " ms", s.ok ? "OK" : "dépassé"]);
|
|
}
|
|
const gzipTotal = files.reduce((sum, f) => sum + f.gzipBytes, 0);
|
|
addRow(["Taille compressée", "", (gzipTotal / 1e6).toFixed(1).replace(".", ",") + " Mo",
|
|
DOWNLOAD_LIMIT_BYTES / 1e6 + " Mo", gzipTotal < DOWNLOAD_LIMIT_BYTES ? "OK" : "dépassé"]);
|
|
|
|
$("report").value = formatReport({
|
|
environment: navigator.userAgent, files, loadMs, memoryBytes, times,
|
|
});
|
|
$("results").hidden = false;
|
|
$("status").textContent = "Mesure terminée.";
|
|
}
|
|
|
|
// Copie dans le presse-papiers. L'API moderne n'existe que sur les pages
|
|
// sécurisées (https ou localhost) : sur le réseau local en http, on
|
|
// sélectionne le texte et on utilise l'ancienne commande de copie.
|
|
async function copyReport() {
|
|
try {
|
|
if (navigator.clipboard) {
|
|
await navigator.clipboard.writeText($("report").value);
|
|
} else {
|
|
$("report").select();
|
|
if (!document.execCommand("copy")) {
|
|
throw new Error();
|
|
}
|
|
}
|
|
$("copy-status").textContent = "Rapport copié : collez-le dans la conversation.";
|
|
} catch {
|
|
$("report").select();
|
|
$("copy-status").textContent = "Copie impossible : le texte est sélectionné, "
|
|
+ "copiez-le à la main (appui long, puis Copier).";
|
|
}
|
|
}
|
|
|
|
$("start").onclick = () => measure().catch((error) => {
|
|
$("status").textContent = "La mesure a échoué : " + error.message;
|
|
$("status").classList.add("error");
|
|
$("start").disabled = false;
|
|
});
|
|
$("copy").onclick = copyReport;
|