/ proof-of-concept / plate-dynamics-lab
Sign in
proof-of-concept / plate-dynamics-lab
plate-dynamics-lab / packages / core / src / cost.ts
38 lines · 1.6 KBBlameHistoryRaw
1import type { MethodDefinition } from "./methods/types";
3/**
4 * Wall-time estimate for grid-based methods. This is a calibration, not
5 * physics: the plan (cell count, steps) comes from the method's plan function
6 * running in numbl; only the seconds-per-cell-step constant lives here, seeded
7 * from the method definition and refined by each finished run. The GPU path of
8 * a method is calibrated separately: its cost is nearly independent of the
9 * cell count at the grids used here, so its per-cell-step rate is a stand-in
10 * that the measurements correct for the grid in use.
11 */
12export class CostModel {
13 private us = new Map<string, number>();
14 private readonly overheadMs = 150;
16 private key(method: MethodDefinition, gpu: boolean): string {
17 return gpu ? `${method.id}:gpu` : method.id;
18 }
20 private rate(method: MethodDefinition, gpu: boolean): number {
21 return (
22 this.us.get(this.key(method, gpu)) ??
23 (gpu ? (method.gpu?.usPerCellStep ?? method.usPerCellStep) : method.usPerCellStep)
24 );
25 }
27 estimateMs(method: MethodDefinition, cellSteps: number, gpu = false): number {
28 return (cellSteps * this.rate(method, gpu)) / 1000 + this.overheadMs;
29 }
31 /** Blend a measurement in, weighting recent runs more. */
32 observe(method: MethodDefinition, cellSteps: number, wallMs: number, gpu = false): void {
33 if (cellSteps <= 0 || wallMs <= 0) return;
34 const measured = ((wallMs - this.overheadMs) * 1000) / cellSteps;
35 if (!Number.isFinite(measured) || measured <= 0) return;
36 this.us.set(this.key(method, gpu), 0.5 * this.rate(method, gpu) + 0.5 * measured);
37 }
↑↓ move↵openescclose