1/// <reference lib="webworker" />
2// The compute worker: runs solves off the main thread. One request at a
3// time; requests queue in the message queue while a sweep runs. numbl
4// solvers run synchronously here; a WebGPU solver runs on the worker's own
5// device, which is why the handler is asynchronous.
7import { getInstance } from "../problems/laplace2d/spec";
8import { getSolver } from "../solvers";
9import { runSweep } from "../harness/sweep";
10import { runPoint, type RunPoint } from "../harness/runner";
11import { runPointGpu, runSweepGpu } from "../harness/webgpuRun";
12import { toResultPoint, type ResultPoint } from "../harness/resultSchema";
13import { DEFAULT_TIMING, type TimingPolicy } from "../harness/timing";
14import { matlabBase, solverSource } from "./matlabSources";
16export interface SweepRequest {
17 type: "sweep";
18 id: number;
19 instanceId: string;
20 solverId: string;
21 timing: TimingPolicy;
22}
24export interface SolutionRequest {
25 type: "solution";
26 id: number;
27 instanceId: string;
28 solverId: string;
29 n: number;
30}
32export type WorkerRequest = SweepRequest | SolutionRequest;
34export type WorkerResponse =
35 | { type: "point"; id: number; point: ResultPoint; index: number; total: number }
36 | { type: "sweepDone"; id: number; points: ResultPoint[] }
37 | {
38 type: "solutionDone";
39 id: number;
40 point: ResultPoint;
41 uGrid: Float64Array;
42 }
43 | { type: "error"; id: number; message: string };
45self.onmessage = async (e: MessageEvent<WorkerRequest>) => {
46 const msg = e.data;
47 try {
48 if (msg.type === "sweep") {
49 const solver = getSolver(msg.solverId);
50 const instance = getInstance(msg.instanceId);
51 const onPoint = (p: RunPoint, index: number, total: number) => {
52 const resp: WorkerResponse = {
53 type: "point",
54 id: msg.id,
55 point: toResultPoint(p),
56 index,
57 total,
58 };
59 postMessage(resp);
60 };
61 const points =
62 solver.runtime === "webgpu"
63 ? await runSweepGpu({ instance, solver, timing: msg.timing, onPoint })
64 : runSweep({
65 instance,
66 solver,
67 sources: { ...matlabBase(), solver: solverSource(msg.solverId) },
68 timing: msg.timing,
69 onPoint,
70 });
71 const resp: WorkerResponse = {
72 type: "sweepDone",
73 id: msg.id,
74 points: points.map(toResultPoint),
75 };
76 postMessage(resp);
77 } else if (msg.type === "solution") {
78 const solver = getSolver(msg.solverId);
79 const timing = { ...DEFAULT_TIMING, minTimedRuns: 1, timeBudgetSeconds: 0 };
80 const p =
81 solver.runtime === "webgpu"
82 ? await runPointGpu({
83 instance: getInstance(msg.instanceId),
84 solverId: msg.solverId,
85 n: msg.n,
86 timing,
87 wantGrid: true,
88 })
89 : runPoint({
90 instance: getInstance(msg.instanceId),
91 n: msg.n,
92 timing,
93 wantGrid: true,
94 sources: { ...matlabBase(), solver: solverSource(msg.solverId) },
95 });
96 if (!p.uGrid) throw new Error("solver returned no grid values");
97 const resp: WorkerResponse = {
98 type: "solutionDone",
99 id: msg.id,
100 point: toResultPoint(p),
101 uGrid: p.uGrid,
102 };
103 postMessage(resp, { transfer: [p.uGrid.buffer] });
104 }
105 } catch (err) {
106 const resp: WorkerResponse = {
107 type: "error",
108 id: msg.id,
109 message: err instanceof Error ? err.message : String(err),
110 };
111 postMessage(resp);
112 }
113};