/** * The optional CPU comparison: run the very same script through numbl's * normal engine (interpreter + its own JIT) in a worker numbl manages. * * tic/toc, fprintf and disp behave natively there, so the output — including * the "Elapsed time is ..." lines — is directly comparable to the GPU pane * and to real MATLAB. numbl computes in f64, which is also a useful * cross-check on the GPU's f32 results. * * A fresh session per run keeps workspace state from leaking between runs. * mip is disabled: sandbox scripts are self-contained MATLAB, and skipping * the bootstrap keeps first-run latency down. */ import { createNumblSession } from 'numbl/browser'; export interface CpuRunResult { output: string; /** Wall time of the whole script, as seen from the host. */ totalSeconds: number; error?: string; aborted?: boolean; } export interface CpuRunHandle { result: Promise; /** Cooperative stop (needs cross-origin isolation to preempt loops). */ cancel: () => void; } export function runOnCpu( source: string, onOutput?: (text: string) => void, ): CpuRunHandle { let disposed = false; let sessionRef: { dispose(): void; interrupt(): void } | null = null; const result = (async (): Promise => { let output = ''; const session = await createNumblSession({ mip: false, persistSystem: false, displayResults: true, onOutput: (text: string) => { output += text; onOutput?.(text); }, }); sessionRef = session; if (disposed) { session.dispose(); return { output: '', totalSeconds: 0, aborted: true }; } const t0 = performance.now(); try { const res = await session.execute(source); const totalSeconds = (performance.now() - t0) / 1000; return { output, totalSeconds, error: res.ok ? undefined : (res.error ?? 'numbl error'), aborted: res.aborted === true, }; } finally { session.dispose(); } })(); return { result, cancel: () => { disposed = true; sessionRef?.interrupt(); sessionRef?.dispose(); }, }; }