baberu-ocr-webgpu / webgpu-smoke.html
ameraino11's picture
Add optimized unified Gather WebGPU models
8020c75 verified
Raw
History Blame Contribute Delete
4.56 kB
<!doctype html>
<meta charset="utf-8" />
<title>Baberu FP32 decoder WebGPU smoke test</title>
<style>
body {
background: #111;
color: #eee;
font: 14px/1.45 ui-monospace, monospace;
margin: 24px;
}
pre { white-space: pre-wrap; }
</style>
<h1>Baberu FP32 decoder WebGPU smoke test</h1>
<pre id="log">Starting…</pre>
<script type="module">
import * as ort from "./.cache/ort/ort.webgpu.min.mjs";
const output = document.querySelector("#log");
const lines = [];
const log = (message) => {
lines.push(`${new Date().toISOString()} ${message}`);
output.textContent = lines.join("\n");
console.log(message);
};
const timed = async (label, operation) => {
const started = performance.now();
const result = await operation();
log(`PASS ${label} (${(performance.now() - started).toFixed(1)} ms)`);
return result;
};
window.addEventListener("error", (event) => log(`FAIL ${event.error?.stack ?? event.message}`));
window.addEventListener("unhandledrejection", (event) => log(`FAIL ${event.reason?.stack ?? event.reason}`));
ort.env.wasm.numThreads = 1;
ort.env.wasm.wasmPaths = "/.cache/ort/";
log(`navigator.gpu=${Boolean(navigator.gpu)} secure=${window.isSecureContext}`);
if (!navigator.gpu) throw new Error("WebGPU is unavailable");
const cacheNames = [
...Array.from({ length: 6 }, (_, index) => `present_k${index}`),
...Array.from({ length: 6 }, (_, index) => `present_v${index}`),
];
const parameters = new URLSearchParams(location.search);
const gpuCache = parameters.get("cache") !== "cpu";
const stepCount = Number(parameters.get("steps") ?? 5);
log(`gpuCache=${gpuCache}`);
log(`stepCount=${stepCount}`);
const preferredOutputLocation = Object.fromEntries([
["logits", "cpu"],
...cacheNames.map((name) => [name, gpuCache ? "gpu-buffer" : "cpu"]),
]);
const sessionOptions = {
executionProviders: ["webgpu"],
preferredOutputLocation,
logSeverityLevel: 2,
};
const createSession = async (path) => {
const bytes = new Uint8Array(await (await fetch(path)).arrayBuffer());
return ort.InferenceSession.create(bytes, sessionOptions);
};
const prefill = await timed("prefill session creation", () =>
createSession("./output/decoder_prefill_fp32.onnx")
);
const visionEmbeds = new ort.Tensor(
"float32",
new Float32Array(1 * 256 * 512),
[1, 256, 512]
);
const argmax = (values) => {
let result = 0;
for (let index = 1; index < values.length; index += 1) {
if (values[index] > values[result]) result = index;
}
return result;
};
const prefillResult = await timed("prefill execution", () =>
prefill.run({ vision_embeds: visionEmbeds })
);
log(`prefill logits=${prefillResult.logits.location} ${JSON.stringify(prefillResult.logits.dims)}`);
log(`prefill cache=${prefillResult.present_k0.location} ${JSON.stringify(prefillResult.present_k0.dims)}`);
const logits = prefillResult.logits.data;
log(`prefill token=${argmax(logits)} min=${Math.min(...logits)} max=${Math.max(...logits)} mean=${logits.reduce((sum, value) => sum + value, 0) / logits.length} sample=${Array.from(logits.slice(0, 10)).join(",")}`);
const step = await timed("step session creation", () =>
createSession("./output/decoder_step_fp32.onnx")
);
const oneHot = (token) => {
const values = new Float32Array(14630);
values[token] = 1;
return new ort.Tensor("float32", values, [1, 1, 14630]);
};
let token = argmax(prefillResult.logits.data);
let cacheResult = prefillResult;
for (let iteration = 0; iteration < stepCount; iteration += 1) {
const stepFeeds = {
token_one_hot: oneHot(token),
position_ids: new ort.Tensor("int32", new Int32Array([257 + iteration]), [1, 1]),
};
for (let index = 0; index < 6; index += 1) {
stepFeeds[`past_k${index}`] = cacheResult[`present_k${index}`];
stepFeeds[`past_v${index}`] = cacheResult[`present_v${index}`];
}
const stepResult = await timed(
`step ${iteration + 1} execution with ${gpuCache ? "GPU" : "CPU"} KV cache`,
() => step.run(stepFeeds)
);
token = argmax(stepResult.logits.data);
log(`step ${iteration + 1} token=${token} cache=${stepResult.present_k0.location} ${JSON.stringify(stepResult.present_k0.dims)}`);
for (const name of cacheNames) cacheResult[name].dispose();
cacheResult.logits.dispose();
stepFeeds.token_one_hot.dispose();
stepFeeds.position_ids.dispose();
cacheResult = stepResult;
}
for (const name of cacheNames) cacheResult[name].dispose();
cacheResult.logits.dispose();
visionEmbeds.dispose();
await prefill.release();
await step.release();
log("DONE");
</script>