/ concept-collection / fastandaccurate
Sign in
concept-collection / fastandaccurate
fastandaccurate / src / harness / runner.ts
89 lines · 2.9 KBBlameHistoryRaw
1// Runs one solver at one resolution on one instance and measures it.
2// The timing protocol (see docs/problems/laplace-dirichlet-2d.md): two
3// untimed warmup runs absorb JIT compilation, then `repeats` timed runs
4// of which the fastest is reported. The minimum is the least contaminated
5// estimator of the solver's own cost, since everything that interferes
6// (scheduling, residual compilation, other load) only ever adds time; all
7// the individual timings are recorded too. The first warmup is timed and
8// reported separately as the cold time. All timing is MATLAB tic/toc
9// inside the numbl session, so browser and node measure the same thing.
11import { runNumblScript } from "./numblRun";
12import type { Laplace2dInstance } from "../problems/laplace2d/spec";
13import { evalErrors } from "../problems/laplace2d/exact";
15export interface MatlabSources {
16 /** build_problem.m source */
17 buildProblem: string;
18 /** laplace2d_bdata.m source */
19 bdata: string;
20 /** solver.m source of the solver under test */
21 solver: string;
24export interface RunPointRequest {
25 instance: Laplace2dInstance;
26 n: number;
27 /** Timed repeats after the warmups (default 5). */
28 repeats?: number;
29 /** Also evaluate the solution on the visualization grid. */
30 wantGrid?: boolean;
31 sources: MatlabSources;
34export interface RunPoint {
35 n: number;
36 /** The fastest of the timed runs. */
37 solveSeconds: number;
38 solveSecondsAll: number[];
39 coldSeconds: number;
40 relMax: number;
41 relL2: number;
42 uEval: Float64Array;
43 uGrid: Float64Array | null;
46function numLiteral(x: number): string {
47 if (!Number.isFinite(x)) throw new Error(`bad numeric literal: ${x}`);
48 return String(x);
51export function runPoint(req: RunPointRequest): RunPoint {
52 const { instance, n } = req;
53 const repeats = req.repeats ?? 5;
54 const wantGrid = req.wantGrid ?? false;
55 const main = [
56 "% generated by the fastandaccurate harness",
57 `prob = build_problem(${numLiteral(instance.a)}, ${numLiteral(instance.k)}, ${numLiteral(instance.d)}, ${wantGrid ? 1 : 0});`,
58 `tic; out = solver(prob, ${n}); res_cold = toc;`,
59 `out = solver(prob, ${n});`,
60 `res_times = zeros(${repeats}, 1);`,
61 `for irep = 1:${repeats}`,
62 ` tic; out = solver(prob, ${n}); res_times(irep) = toc;`,
63 "end",
64 "res_ueval = out.uEval;",
65 "res_ugrid = out.uGrid;",
66 "",
67 ].join("\n");
68 const { vars } = runNumblScript(
69 main,
70 {
71 "build_problem.m": req.sources.buildProblem,
72 "laplace2d_bdata.m": req.sources.bdata,
73 "solver.m": req.sources.solver,
74 },
75 ["res_cold", "res_times", "res_ueval", "res_ugrid"]
76 );
77 const times = Array.from(vars.res_times);
78 const { relMax, relL2 } = evalErrors(instance, vars.res_ueval);
79 return {
80 n,
81 solveSeconds: Math.min(...times),
82 solveSecondsAll: times,
83 coldSeconds: vars.res_cold[0],
84 relMax,
85 relL2,
86 uEval: vars.res_ueval,
87 uGrid: wantGrid ? vars.res_ugrid : null,
88 };
moveopenescclose