webgpu-bench 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/dist/benchmarks/common.d.ts +71 -0
- package/dist/benchmarks/common.d.ts.map +1 -0
- package/dist/benchmarks/common.js +104 -0
- package/dist/benchmarks/common.js.map +1 -0
- package/dist/benchmarks/flopsCommon.d.ts +52 -0
- package/dist/benchmarks/flopsCommon.d.ts.map +1 -0
- package/dist/benchmarks/flopsCommon.js +78 -0
- package/dist/benchmarks/flopsCommon.js.map +1 -0
- package/dist/benchmarks/flopsConvert.d.ts +18 -0
- package/dist/benchmarks/flopsConvert.d.ts.map +1 -0
- package/dist/benchmarks/flopsConvert.js +61 -0
- package/dist/benchmarks/flopsConvert.js.map +1 -0
- package/dist/benchmarks/flopsF16.d.ts +12 -0
- package/dist/benchmarks/flopsF16.d.ts.map +1 -0
- package/dist/benchmarks/flopsF16.js +54 -0
- package/dist/benchmarks/flopsF16.js.map +1 -0
- package/dist/benchmarks/flopsF32.d.ts +12 -0
- package/dist/benchmarks/flopsF32.d.ts.map +1 -0
- package/dist/benchmarks/flopsF32.js +50 -0
- package/dist/benchmarks/flopsF32.js.map +1 -0
- package/dist/benchmarks/flopsI8.d.ts +16 -0
- package/dist/benchmarks/flopsI8.d.ts.map +1 -0
- package/dist/benchmarks/flopsI8.js +83 -0
- package/dist/benchmarks/flopsI8.js.map +1 -0
- package/dist/benchmarks/flopsMath.d.ts +28 -0
- package/dist/benchmarks/flopsMath.d.ts.map +1 -0
- package/dist/benchmarks/flopsMath.js +98 -0
- package/dist/benchmarks/flopsMath.js.map +1 -0
- package/dist/benchmarks/streamBandwidth.d.ts +17 -0
- package/dist/benchmarks/streamBandwidth.d.ts.map +1 -0
- package/dist/benchmarks/streamBandwidth.js +78 -0
- package/dist/benchmarks/streamBandwidth.js.map +1 -0
- package/dist/data/generate.d.ts +23 -0
- package/dist/data/generate.d.ts.map +1 -0
- package/dist/data/generate.js +41 -0
- package/dist/data/generate.js.map +1 -0
- package/dist/gpu/benchmarkRunner.d.ts +98 -0
- package/dist/gpu/benchmarkRunner.d.ts.map +1 -0
- package/dist/gpu/benchmarkRunner.js +178 -0
- package/dist/gpu/benchmarkRunner.js.map +1 -0
- package/dist/gpu/buffers.d.ts +8 -0
- package/dist/gpu/buffers.d.ts.map +1 -0
- package/dist/gpu/buffers.js +41 -0
- package/dist/gpu/buffers.js.map +1 -0
- package/dist/gpu/context.d.ts +16 -0
- package/dist/gpu/context.d.ts.map +1 -0
- package/dist/gpu/context.js +71 -0
- package/dist/gpu/context.js.map +1 -0
- package/dist/gpu/timing.d.ts +20 -0
- package/dist/gpu/timing.d.ts.map +1 -0
- package/dist/gpu/timing.js +55 -0
- package/dist/gpu/timing.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/sampling.d.ts +96 -0
- package/dist/sampling.d.ts.map +1 -0
- package/dist/sampling.js +239 -0
- package/dist/sampling.js.map +1 -0
- package/dist/shaders/flopsF16Mat4.d.ts +7 -0
- package/dist/shaders/flopsF16Mat4.d.ts.map +1 -0
- package/dist/shaders/flopsF16Mat4.js +40 -0
- package/dist/shaders/flopsF16Mat4.js.map +1 -0
- package/dist/shaders/flopsF16Matvec.d.ts +14 -0
- package/dist/shaders/flopsF16Matvec.d.ts.map +1 -0
- package/dist/shaders/flopsF16Matvec.js +58 -0
- package/dist/shaders/flopsF16Matvec.js.map +1 -0
- package/dist/shaders/flopsF16Scalar.d.ts +8 -0
- package/dist/shaders/flopsF16Scalar.d.ts.map +1 -0
- package/dist/shaders/flopsF16Scalar.js +71 -0
- package/dist/shaders/flopsF16Scalar.js.map +1 -0
- package/dist/shaders/flopsF16Vec4.d.ts +7 -0
- package/dist/shaders/flopsF16Vec4.d.ts.map +1 -0
- package/dist/shaders/flopsF16Vec4.js +35 -0
- package/dist/shaders/flopsF16Vec4.js.map +1 -0
- package/dist/shaders/flopsF32Div.d.ts +10 -0
- package/dist/shaders/flopsF32Div.d.ts.map +1 -0
- package/dist/shaders/flopsF32Div.js +71 -0
- package/dist/shaders/flopsF32Div.js.map +1 -0
- package/dist/shaders/flopsF32F16Convert.d.ts +11 -0
- package/dist/shaders/flopsF32F16Convert.d.ts.map +1 -0
- package/dist/shaders/flopsF32F16Convert.js +73 -0
- package/dist/shaders/flopsF32F16Convert.js.map +1 -0
- package/dist/shaders/flopsF32Log.d.ts +11 -0
- package/dist/shaders/flopsF32Log.d.ts.map +1 -0
- package/dist/shaders/flopsF32Log.js +71 -0
- package/dist/shaders/flopsF32Log.js.map +1 -0
- package/dist/shaders/flopsF32Mat4.d.ts +9 -0
- package/dist/shaders/flopsF32Mat4.d.ts.map +1 -0
- package/dist/shaders/flopsF32Mat4.js +38 -0
- package/dist/shaders/flopsF32Mat4.js.map +1 -0
- package/dist/shaders/flopsF32Matvec.d.ts +14 -0
- package/dist/shaders/flopsF32Matvec.d.ts.map +1 -0
- package/dist/shaders/flopsF32Matvec.js +58 -0
- package/dist/shaders/flopsF32Matvec.js.map +1 -0
- package/dist/shaders/flopsF32Pow.d.ts +10 -0
- package/dist/shaders/flopsF32Pow.d.ts.map +1 -0
- package/dist/shaders/flopsF32Pow.js +71 -0
- package/dist/shaders/flopsF32Pow.js.map +1 -0
- package/dist/shaders/flopsF32Rsqrt.d.ts +12 -0
- package/dist/shaders/flopsF32Rsqrt.d.ts.map +1 -0
- package/dist/shaders/flopsF32Rsqrt.js +72 -0
- package/dist/shaders/flopsF32Rsqrt.js.map +1 -0
- package/dist/shaders/flopsF32Scalar.d.ts +11 -0
- package/dist/shaders/flopsF32Scalar.d.ts.map +1 -0
- package/dist/shaders/flopsF32Scalar.js +72 -0
- package/dist/shaders/flopsF32Scalar.js.map +1 -0
- package/dist/shaders/flopsF32Sincos.d.ts +9 -0
- package/dist/shaders/flopsF32Sincos.d.ts.map +1 -0
- package/dist/shaders/flopsF32Sincos.js +68 -0
- package/dist/shaders/flopsF32Sincos.js.map +1 -0
- package/dist/shaders/flopsF32Sqrt.d.ts +9 -0
- package/dist/shaders/flopsF32Sqrt.d.ts.map +1 -0
- package/dist/shaders/flopsF32Sqrt.js +69 -0
- package/dist/shaders/flopsF32Sqrt.js.map +1 -0
- package/dist/shaders/flopsF32Vec4.d.ts +9 -0
- package/dist/shaders/flopsF32Vec4.d.ts.map +1 -0
- package/dist/shaders/flopsF32Vec4.js +33 -0
- package/dist/shaders/flopsF32Vec4.js.map +1 -0
- package/dist/shaders/flopsI32Div.d.ts +10 -0
- package/dist/shaders/flopsI32Div.d.ts.map +1 -0
- package/dist/shaders/flopsI32Div.js +71 -0
- package/dist/shaders/flopsI32Div.js.map +1 -0
- package/dist/shaders/flopsI32F16Convert.d.ts +10 -0
- package/dist/shaders/flopsI32F16Convert.d.ts.map +1 -0
- package/dist/shaders/flopsI32F16Convert.js +71 -0
- package/dist/shaders/flopsI32F16Convert.js.map +1 -0
- package/dist/shaders/flopsI32F32Convert.d.ts +10 -0
- package/dist/shaders/flopsI32F32Convert.d.ts.map +1 -0
- package/dist/shaders/flopsI32F32Convert.js +71 -0
- package/dist/shaders/flopsI32F32Convert.js.map +1 -0
- package/dist/shaders/flopsI8Dp4a.d.ts +23 -0
- package/dist/shaders/flopsI8Dp4a.d.ts.map +1 -0
- package/dist/shaders/flopsI8Dp4a.js +49 -0
- package/dist/shaders/flopsI8Dp4a.js.map +1 -0
- package/dist/shaders/flopsI8Mat4.d.ts +9 -0
- package/dist/shaders/flopsI8Mat4.d.ts.map +1 -0
- package/dist/shaders/flopsI8Mat4.js +41 -0
- package/dist/shaders/flopsI8Mat4.js.map +1 -0
- package/dist/shaders/flopsI8Matvec.d.ts +16 -0
- package/dist/shaders/flopsI8Matvec.d.ts.map +1 -0
- package/dist/shaders/flopsI8Matvec.js +57 -0
- package/dist/shaders/flopsI8Matvec.js.map +1 -0
- package/dist/shaders/flopsI8MatvecDp4a.d.ts +17 -0
- package/dist/shaders/flopsI8MatvecDp4a.d.ts.map +1 -0
- package/dist/shaders/flopsI8MatvecDp4a.js +60 -0
- package/dist/shaders/flopsI8MatvecDp4a.js.map +1 -0
- package/dist/shaders/flopsI8Scalar.d.ts +9 -0
- package/dist/shaders/flopsI8Scalar.d.ts.map +1 -0
- package/dist/shaders/flopsI8Scalar.js +70 -0
- package/dist/shaders/flopsI8Scalar.js.map +1 -0
- package/dist/shaders/flopsI8Vec4.d.ts +7 -0
- package/dist/shaders/flopsI8Vec4.d.ts.map +1 -0
- package/dist/shaders/flopsI8Vec4.js +31 -0
- package/dist/shaders/flopsI8Vec4.js.map +1 -0
- package/dist/shaders/flopsU32PackUnpack.d.ts +11 -0
- package/dist/shaders/flopsU32PackUnpack.d.ts.map +1 -0
- package/dist/shaders/flopsU32PackUnpack.js +55 -0
- package/dist/shaders/flopsU32PackUnpack.js.map +1 -0
- package/dist/shaders/streamRead.d.ts +10 -0
- package/dist/shaders/streamRead.d.ts.map +1 -0
- package/dist/shaders/streamRead.js +33 -0
- package/dist/shaders/streamRead.js.map +1 -0
- package/dist/shaders/streamWrite.d.ts +8 -0
- package/dist/shaders/streamWrite.d.ts.map +1 -0
- package/dist/shaders/streamWrite.js +29 -0
- package/dist/shaders/streamWrite.js.map +1 -0
- package/dist/stats.d.ts +31 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +67 -0
- package/dist/stats.js.map +1 -0
- package/dist/suite.d.ts +25 -0
- package/dist/suite.d.ts.map +1 -0
- package/dist/suite.js +196 -0
- package/dist/suite.js.map +1 -0
- package/dist/types.d.ts +171 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +45 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { TimingMethod } from '../types.ts';
|
|
2
|
+
/** Knobs controlling how one timed measurement of a kernel is taken. */
|
|
3
|
+
export interface MeasurementConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Target duration of a single measurement (one submitted command buffer),
|
|
6
|
+
* in ms; sets how many dispatches get batched. Default 100.
|
|
7
|
+
*/
|
|
8
|
+
targetMs?: number;
|
|
9
|
+
/**
|
|
10
|
+
* Target duration of a single *dispatch*, in ms. A dispatch can't be
|
|
11
|
+
* preempted, so a long one freezes the display; kernels that expose a
|
|
12
|
+
* work knob get it calibrated so one dispatch lands near this. Default 10.
|
|
13
|
+
*/
|
|
14
|
+
targetDispatchMs?: number;
|
|
15
|
+
/** Discarded measurements taken after calibration and before timing starts. Default 1. */
|
|
16
|
+
warmups?: number;
|
|
17
|
+
/** Hard cap on how many dispatches get batched into one measurement (guards against runaway calibration). */
|
|
18
|
+
maxIterations?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare const DEFAULT_MEASUREMENT: Required<MeasurementConfig>;
|
|
21
|
+
/**
|
|
22
|
+
* A kernel's tunable per-dispatch work (for the raw-FLOPS kernels: the
|
|
23
|
+
* in-shader loop trip count). Lets the sampler size a single dispatch to
|
|
24
|
+
* `targetDispatchMs` on whatever GPU it finds itself on, instead of a fixed
|
|
25
|
+
* count that's a few ms on a desktop and a second on a phone.
|
|
26
|
+
*/
|
|
27
|
+
export interface WorkKnob {
|
|
28
|
+
/** Smallest sensible work per dispatch (also the first calibration probe, so keep it cheap). */
|
|
29
|
+
min: number;
|
|
30
|
+
/** Largest work per dispatch (the kernel's own default; bounds numerical range and per-thread setup amortisation). */
|
|
31
|
+
max: number;
|
|
32
|
+
/** Make subsequent dispatches use this much work (e.g. rewrite the params uniform). */
|
|
33
|
+
apply(work: number): void;
|
|
34
|
+
}
|
|
35
|
+
export interface KernelHarness extends MeasurementConfig {
|
|
36
|
+
device: GPUDevice;
|
|
37
|
+
/** Records `iterations` back-to-back dispatches (same pipeline/bind group) into the pass. */
|
|
38
|
+
encode: (pass: GPUComputePassEncoder, iterations: number) => void;
|
|
39
|
+
useTimestamps: boolean;
|
|
40
|
+
/** Present for kernels whose per-dispatch work can be resized at runtime. */
|
|
41
|
+
work?: WorkKnob;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Takes individual timed measurements of one kernel on demand, so a
|
|
45
|
+
* scheduler can interleave many kernels round-robin (see `runSampling`)
|
|
46
|
+
* instead of hammering one kernel until it converges. Holds the GPU timer
|
|
47
|
+
* and the calibrated batch size between calls.
|
|
48
|
+
*
|
|
49
|
+
* Lifecycle: `calibrate()` once (sizes the dispatch and the batch, runs the
|
|
50
|
+
* warmups), then `sample()` as often as wanted, then `destroy()`.
|
|
51
|
+
*/
|
|
52
|
+
export declare class KernelSampler {
|
|
53
|
+
private readonly h;
|
|
54
|
+
private readonly timer;
|
|
55
|
+
private iterations;
|
|
56
|
+
private currentWork;
|
|
57
|
+
/** Cleared once the GPU timestamps have disagreed with wall clock `TIMESTAMP_STRIKES_TO_DEMOTE` times running. */
|
|
58
|
+
private trustTimestamps;
|
|
59
|
+
private timestampStrikes;
|
|
60
|
+
/** Wall time of an empty submit (min of `OVERHEAD_PROBES`): submit, scheduling and readback, no GPU work. */
|
|
61
|
+
private overheadMs;
|
|
62
|
+
constructor(h: KernelHarness);
|
|
63
|
+
get timingMethod(): TimingMethod;
|
|
64
|
+
/** Number of dispatches batched into each measurement; 0 until calibrated. */
|
|
65
|
+
get innerIterations(): number;
|
|
66
|
+
/** Per-dispatch work the kernel was calibrated to; undefined if it has no work knob. */
|
|
67
|
+
get work(): number | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* Sizes the kernel so one dispatch takes about `targetDispatchMs` (when it
|
|
70
|
+
* has a work knob: start from the cheapest probe and ramp up, never more
|
|
71
|
+
* than 4x per step, so even a very slow GPU never gets handed a long
|
|
72
|
+
* dispatch), then batches dispatches to fill `targetMs`, then runs the
|
|
73
|
+
* discarded warmups. The probes double as pipeline warm-up.
|
|
74
|
+
*
|
|
75
|
+
* Batch sizing uses *wall-clock* time, never the GPU timestamps: wall
|
|
76
|
+
* clock includes submit/readback overhead so it can only over-estimate,
|
|
77
|
+
* which errs toward smaller batches — whereas a broken timestamp (see
|
|
78
|
+
* `TIMESTAMP_MAX_UNDERREPORT`) would size a batch tens of times too large
|
|
79
|
+
* and hang the browser. The work ramp does use the GPU time while it's
|
|
80
|
+
* trusted (a single dispatch's wall time is mostly readback overhead, so
|
|
81
|
+
* sizing from it leaves dispatches far too small), which is safe there
|
|
82
|
+
* because growth is capped at 4x per step: a bogus reading can at worst
|
|
83
|
+
* overshoot one step before the wall-clock cross-check demotes it.
|
|
84
|
+
*/
|
|
85
|
+
calibrate(): Promise<void>;
|
|
86
|
+
/** One timed measurement: per-op time in ms. Requires `calibrate()` first. */
|
|
87
|
+
sample(): Promise<number>;
|
|
88
|
+
destroy(): void;
|
|
89
|
+
/**
|
|
90
|
+
* Submits `iterations` dispatches as one command buffer and waits for
|
|
91
|
+
* them. Always returns the wall-clock time; returns the GPU-timestamp
|
|
92
|
+
* time too while the timestamps are trusted. An implausibly small reading
|
|
93
|
+
* (see `TIMESTAMP_MAX_UNDERREPORT`) is replaced by wall clock, and the
|
|
94
|
+
* sampler is demoted to wall-clock for good on the second in a row.
|
|
95
|
+
*/
|
|
96
|
+
private measureRaw;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=benchmarkRunner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmarkRunner.d.ts","sourceRoot":"","sources":["../../src/gpu/benchmarkRunner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6GAA6G;IAC7G,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,iBAAiB,CAK3D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,gGAAgG;IAChG,GAAG,EAAE,MAAM,CAAC;IACZ,sHAAsH;IACtH,GAAG,EAAE,MAAM,CAAC;IACZ,uFAAuF;IACvF,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,aAAc,SAAQ,iBAAiB;IACtD,MAAM,EAAE,SAAS,CAAC;IAClB,6FAA6F;IAC7F,MAAM,EAAE,CAAC,IAAI,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAClE,aAAa,EAAE,OAAO,CAAC;IACvB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB;AAsBD;;;;;;;;GAQG;AACH,qBAAa,aAAa;IAUZ,OAAO,CAAC,QAAQ,CAAC,CAAC;IAT9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAW;IACjC,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,WAAW,CAAqB;IACxC,kHAAkH;IAClH,OAAO,CAAC,eAAe,CAAU;IACjC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,6GAA6G;IAC7G,OAAO,CAAC,UAAU,CAAK;gBAEM,CAAC,EAAE,aAAa;IAK7C,IAAI,YAAY,IAAI,YAAY,CAE/B;IAED,8EAA8E;IAC9E,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,wFAAwF;IACxF,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAE7B;IAED;;;;;;;;;;;;;;;;OAgBG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAsDhC,8EAA8E;IACxE,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAQ/B,OAAO,IAAI,IAAI;IAIf;;;;;;OAMG;YACW,UAAU;CAgCzB"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { GpuTimer } from "./timing.js";
|
|
2
|
+
export const DEFAULT_MEASUREMENT = {
|
|
3
|
+
targetMs: 100,
|
|
4
|
+
targetDispatchMs: 10,
|
|
5
|
+
warmups: 1,
|
|
6
|
+
maxIterations: 200_000,
|
|
7
|
+
};
|
|
8
|
+
/** Never grow a calibration probe by more than this factor per step, so no probe can run away on a slow GPU. */
|
|
9
|
+
const MAX_PROBE_GROWTH = 4;
|
|
10
|
+
/**
|
|
11
|
+
* GPU timestamps are cross-checked against wall clock on every measurement.
|
|
12
|
+
* Wall clock minus the measured fixed submit/readback overhead is what the
|
|
13
|
+
* GPU actually spent, so a GPU reading under half of that is a broken timer.
|
|
14
|
+
* Safari's `timestamp-query` does exactly this — reporting ~1ms for a ~70ms
|
|
15
|
+
* batch — and trusting it makes calibration batch ~70x too much work into
|
|
16
|
+
* one command buffer, which hangs the browser. Two consecutive failures are
|
|
17
|
+
* required before demoting for good, so a one-off main-thread stall that
|
|
18
|
+
* inflates wall time (GC, a compositor frame) can't cost a kernel its
|
|
19
|
+
* accurate timer.
|
|
20
|
+
*/
|
|
21
|
+
const TIMESTAMP_CHECK_MIN_GPU_MS = 4;
|
|
22
|
+
const TIMESTAMP_MAX_UNDERREPORT = 2;
|
|
23
|
+
const TIMESTAMP_STRIKES_TO_DEMOTE = 2;
|
|
24
|
+
/** Empty submits measured at calibration to estimate the fixed per-measurement overhead. */
|
|
25
|
+
const OVERHEAD_PROBES = 3;
|
|
26
|
+
/**
|
|
27
|
+
* Takes individual timed measurements of one kernel on demand, so a
|
|
28
|
+
* scheduler can interleave many kernels round-robin (see `runSampling`)
|
|
29
|
+
* instead of hammering one kernel until it converges. Holds the GPU timer
|
|
30
|
+
* and the calibrated batch size between calls.
|
|
31
|
+
*
|
|
32
|
+
* Lifecycle: `calibrate()` once (sizes the dispatch and the batch, runs the
|
|
33
|
+
* warmups), then `sample()` as often as wanted, then `destroy()`.
|
|
34
|
+
*/
|
|
35
|
+
export class KernelSampler {
|
|
36
|
+
h;
|
|
37
|
+
timer;
|
|
38
|
+
iterations = 0;
|
|
39
|
+
currentWork;
|
|
40
|
+
/** Cleared once the GPU timestamps have disagreed with wall clock `TIMESTAMP_STRIKES_TO_DEMOTE` times running. */
|
|
41
|
+
trustTimestamps;
|
|
42
|
+
timestampStrikes = 0;
|
|
43
|
+
/** Wall time of an empty submit (min of `OVERHEAD_PROBES`): submit, scheduling and readback, no GPU work. */
|
|
44
|
+
overheadMs = 0;
|
|
45
|
+
constructor(h) {
|
|
46
|
+
this.h = h;
|
|
47
|
+
this.timer = new GpuTimer(h.device, h.useTimestamps);
|
|
48
|
+
this.trustTimestamps = this.timer.supported;
|
|
49
|
+
}
|
|
50
|
+
get timingMethod() {
|
|
51
|
+
return this.trustTimestamps ? 'gpu-timestamp' : 'cpu-wallclock';
|
|
52
|
+
}
|
|
53
|
+
/** Number of dispatches batched into each measurement; 0 until calibrated. */
|
|
54
|
+
get innerIterations() {
|
|
55
|
+
return this.iterations;
|
|
56
|
+
}
|
|
57
|
+
/** Per-dispatch work the kernel was calibrated to; undefined if it has no work knob. */
|
|
58
|
+
get work() {
|
|
59
|
+
return this.currentWork;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Sizes the kernel so one dispatch takes about `targetDispatchMs` (when it
|
|
63
|
+
* has a work knob: start from the cheapest probe and ramp up, never more
|
|
64
|
+
* than 4x per step, so even a very slow GPU never gets handed a long
|
|
65
|
+
* dispatch), then batches dispatches to fill `targetMs`, then runs the
|
|
66
|
+
* discarded warmups. The probes double as pipeline warm-up.
|
|
67
|
+
*
|
|
68
|
+
* Batch sizing uses *wall-clock* time, never the GPU timestamps: wall
|
|
69
|
+
* clock includes submit/readback overhead so it can only over-estimate,
|
|
70
|
+
* which errs toward smaller batches — whereas a broken timestamp (see
|
|
71
|
+
* `TIMESTAMP_MAX_UNDERREPORT`) would size a batch tens of times too large
|
|
72
|
+
* and hang the browser. The work ramp does use the GPU time while it's
|
|
73
|
+
* trusted (a single dispatch's wall time is mostly readback overhead, so
|
|
74
|
+
* sizing from it leaves dispatches far too small), which is safe there
|
|
75
|
+
* because growth is capped at 4x per step: a bogus reading can at worst
|
|
76
|
+
* overshoot one step before the wall-clock cross-check demotes it.
|
|
77
|
+
*/
|
|
78
|
+
async calibrate() {
|
|
79
|
+
const targetMs = this.h.targetMs ?? DEFAULT_MEASUREMENT.targetMs;
|
|
80
|
+
const targetDispatchMs = this.h.targetDispatchMs ?? DEFAULT_MEASUREMENT.targetDispatchMs;
|
|
81
|
+
const warmups = this.h.warmups ?? DEFAULT_MEASUREMENT.warmups;
|
|
82
|
+
const maxIterations = this.h.maxIterations ?? DEFAULT_MEASUREMENT.maxIterations;
|
|
83
|
+
this.overheadMs = Number.POSITIVE_INFINITY;
|
|
84
|
+
for (let i = 0; i < OVERHEAD_PROBES; i++) {
|
|
85
|
+
this.overheadMs = Math.min(this.overheadMs, (await this.measureRaw(0)).wallMs);
|
|
86
|
+
}
|
|
87
|
+
const knob = this.h.work;
|
|
88
|
+
if (knob) {
|
|
89
|
+
let work = Math.max(1, Math.floor(knob.min));
|
|
90
|
+
knob.apply(work);
|
|
91
|
+
const dispatchTime = async () => {
|
|
92
|
+
const { gpuMs, wallMs } = await this.measureRaw(1);
|
|
93
|
+
return gpuMs ?? wallMs;
|
|
94
|
+
};
|
|
95
|
+
let dispatchMs = await dispatchTime();
|
|
96
|
+
// Ramp: the dispatch time is ~linear in work, so extrapolate straight
|
|
97
|
+
// to the target, but cap the growth per step and re-measure.
|
|
98
|
+
while (work < knob.max && dispatchMs < targetDispatchMs) {
|
|
99
|
+
const wanted = (work * targetDispatchMs) / Math.max(dispatchMs, 1e-6);
|
|
100
|
+
const next = Math.min(knob.max, Math.floor(Math.min(wanted, work * MAX_PROBE_GROWTH)));
|
|
101
|
+
if (next <= work)
|
|
102
|
+
break;
|
|
103
|
+
work = next;
|
|
104
|
+
knob.apply(work);
|
|
105
|
+
dispatchMs = await dispatchTime();
|
|
106
|
+
}
|
|
107
|
+
this.currentWork = work;
|
|
108
|
+
}
|
|
109
|
+
// Batch sizing: grow from a single dispatch until one measurement's wall
|
|
110
|
+
// time is near the target. Wall clock over-estimates a tiny dispatch
|
|
111
|
+
// (fixed submit/readback overhead dominates), so the first extrapolation
|
|
112
|
+
// under-shoots and a couple of refinement steps home in from below. The
|
|
113
|
+
// very first measurement never ends the search: it can absorb a one-off
|
|
114
|
+
// stall (lazy shader compile, GC) that would leave the batch at 1.
|
|
115
|
+
let iterations = 1;
|
|
116
|
+
for (let step = 0; step < 4; step++) {
|
|
117
|
+
const { wallMs } = await this.measureRaw(iterations);
|
|
118
|
+
if ((step > 0 && wallMs >= targetMs / 2) || iterations >= maxIterations)
|
|
119
|
+
break;
|
|
120
|
+
const next = Math.min(maxIterations, Math.floor((iterations * targetMs) / Math.max(wallMs, 1e-6)));
|
|
121
|
+
if (next <= iterations)
|
|
122
|
+
break;
|
|
123
|
+
iterations = next;
|
|
124
|
+
}
|
|
125
|
+
this.iterations = iterations;
|
|
126
|
+
for (let i = 0; i < warmups; i++) {
|
|
127
|
+
await this.measureRaw(this.iterations);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/** One timed measurement: per-op time in ms. Requires `calibrate()` first. */
|
|
131
|
+
async sample() {
|
|
132
|
+
if (this.iterations === 0) {
|
|
133
|
+
throw new Error('KernelSampler.sample() called before calibrate()');
|
|
134
|
+
}
|
|
135
|
+
const { gpuMs, wallMs } = await this.measureRaw(this.iterations);
|
|
136
|
+
return (gpuMs ?? wallMs) / this.iterations;
|
|
137
|
+
}
|
|
138
|
+
destroy() {
|
|
139
|
+
this.timer.destroy();
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Submits `iterations` dispatches as one command buffer and waits for
|
|
143
|
+
* them. Always returns the wall-clock time; returns the GPU-timestamp
|
|
144
|
+
* time too while the timestamps are trusted. An implausibly small reading
|
|
145
|
+
* (see `TIMESTAMP_MAX_UNDERREPORT`) is replaced by wall clock, and the
|
|
146
|
+
* sampler is demoted to wall-clock for good on the second in a row.
|
|
147
|
+
*/
|
|
148
|
+
async measureRaw(iterations) {
|
|
149
|
+
const { h, timer } = this;
|
|
150
|
+
const useTimestamps = this.trustTimestamps;
|
|
151
|
+
const encoder = h.device.createCommandEncoder();
|
|
152
|
+
const pass = encoder.beginComputePass({ timestampWrites: useTimestamps ? timer.timestampWrites : undefined });
|
|
153
|
+
h.encode(pass, iterations);
|
|
154
|
+
pass.end();
|
|
155
|
+
if (useTimestamps)
|
|
156
|
+
timer.resolve(encoder);
|
|
157
|
+
const cpuStart = performance.now();
|
|
158
|
+
h.device.queue.submit([encoder.finish()]);
|
|
159
|
+
if (!useTimestamps) {
|
|
160
|
+
await h.device.queue.onSubmittedWorkDone();
|
|
161
|
+
return { gpuMs: null, wallMs: performance.now() - cpuStart };
|
|
162
|
+
}
|
|
163
|
+
const gpuMs = await timer.readElapsedMs();
|
|
164
|
+
const wallMs = performance.now() - cpuStart;
|
|
165
|
+
const gpuWallMs = wallMs - this.overheadMs;
|
|
166
|
+
if (iterations > 0 &&
|
|
167
|
+
gpuWallMs >= TIMESTAMP_CHECK_MIN_GPU_MS &&
|
|
168
|
+
!(gpuMs * TIMESTAMP_MAX_UNDERREPORT >= gpuWallMs)) {
|
|
169
|
+
this.timestampStrikes += 1;
|
|
170
|
+
if (this.timestampStrikes >= TIMESTAMP_STRIKES_TO_DEMOTE)
|
|
171
|
+
this.trustTimestamps = false;
|
|
172
|
+
return { gpuMs: null, wallMs };
|
|
173
|
+
}
|
|
174
|
+
this.timestampStrikes = 0;
|
|
175
|
+
return { gpuMs, wallMs };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=benchmarkRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmarkRunner.js","sourceRoot":"","sources":["../../src/gpu/benchmarkRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Creates a GPU buffer, uploads `data` into it, and returns the buffer. */
|
|
2
|
+
export declare function createUploadedBuffer(device: GPUDevice, data: ArrayBufferView, usage: GPUBufferUsageFlags, label?: string): GPUBuffer;
|
|
3
|
+
export declare function createStorageBuffer(device: GPUDevice, data: ArrayBufferView, label?: string): GPUBuffer;
|
|
4
|
+
export declare function createUniformBuffer(device: GPUDevice, data: ArrayBufferView, label?: string): GPUBuffer;
|
|
5
|
+
export declare function createEmptyStorageBuffer(device: GPUDevice, byteLength: number, label?: string): GPUBuffer;
|
|
6
|
+
/** Reads a storage buffer back to the CPU as a Float32Array (used only for correctness spot-checks, not timing). */
|
|
7
|
+
export declare function readFloat32(device: GPUDevice, buffer: GPUBuffer, count: number): Promise<Float32Array>;
|
|
8
|
+
//# sourceMappingURL=buffers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buffers.d.ts","sourceRoot":"","sources":["../../src/gpu/buffers.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,eAAe,EACrB,KAAK,EAAE,mBAAmB,EAC1B,KAAK,CAAC,EAAE,MAAM,GACb,SAAS,CAUX;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAOvG;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAEvG;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAMzG;AAED,oHAAoH;AACpH,wBAAsB,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAa5G"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Creates a GPU buffer, uploads `data` into it, and returns the buffer. */
|
|
2
|
+
export function createUploadedBuffer(device, data, usage, label) {
|
|
3
|
+
const buffer = device.createBuffer({
|
|
4
|
+
label,
|
|
5
|
+
size: Math.max(4, Math.ceil(data.byteLength / 4) * 4),
|
|
6
|
+
usage,
|
|
7
|
+
mappedAtCreation: true,
|
|
8
|
+
});
|
|
9
|
+
new Uint8Array(buffer.getMappedRange()).set(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
|
|
10
|
+
buffer.unmap();
|
|
11
|
+
return buffer;
|
|
12
|
+
}
|
|
13
|
+
export function createStorageBuffer(device, data, label) {
|
|
14
|
+
return createUploadedBuffer(device, data, GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST, label);
|
|
15
|
+
}
|
|
16
|
+
export function createUniformBuffer(device, data, label) {
|
|
17
|
+
return createUploadedBuffer(device, data, GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, label);
|
|
18
|
+
}
|
|
19
|
+
export function createEmptyStorageBuffer(device, byteLength, label) {
|
|
20
|
+
return device.createBuffer({
|
|
21
|
+
label,
|
|
22
|
+
size: Math.max(4, Math.ceil(byteLength / 4) * 4),
|
|
23
|
+
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/** Reads a storage buffer back to the CPU as a Float32Array (used only for correctness spot-checks, not timing). */
|
|
27
|
+
export async function readFloat32(device, buffer, count) {
|
|
28
|
+
const staging = device.createBuffer({
|
|
29
|
+
size: count * 4,
|
|
30
|
+
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
|
|
31
|
+
});
|
|
32
|
+
const encoder = device.createCommandEncoder();
|
|
33
|
+
encoder.copyBufferToBuffer(buffer, 0, staging, 0, count * 4);
|
|
34
|
+
device.queue.submit([encoder.finish()]);
|
|
35
|
+
await staging.mapAsync(GPUMapMode.READ);
|
|
36
|
+
const result = new Float32Array(staging.getMappedRange().slice(0));
|
|
37
|
+
staging.unmap();
|
|
38
|
+
staging.destroy();
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=buffers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buffers.js","sourceRoot":"","sources":["../../src/gpu/buffers.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAClC,MAAiB,EACjB,IAAqB,EACrB,KAA0B,EAC1B,KAAc;IAEd,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC;QACjC,KAAK;QACL,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACrD,KAAK;QACL,gBAAgB,EAAE,IAAI;KACvB,CAAC,CAAC;IACH,IAAI,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAC3G,MAAM,CAAC,KAAK,EAAE,CAAC;IACf,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,IAAqB,EAAE,KAAc;IAC1F,OAAO,oBAAoB,CACzB,MAAM,EACN,IAAI,EACJ,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,QAAQ,GAAG,cAAc,CAAC,QAAQ,EAC1E,KAAK,CACN,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAiB,EAAE,IAAqB,EAAE,KAAc;IAC1F,OAAO,oBAAoB,CAAC,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AACrG,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAiB,EAAE,UAAkB,EAAE,KAAc;IAC5F,OAAO,MAAM,CAAC,YAAY,CAAC;QACzB,KAAK;QACL,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAChD,KAAK,EAAE,cAAc,CAAC,OAAO,GAAG,cAAc,CAAC,QAAQ,GAAG,cAAc,CAAC,QAAQ;KAClF,CAAC,CAAC;AACL,CAAC;AAED,oHAAoH;AACpH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAiB,EAAE,MAAiB,EAAE,KAAa;IACnF,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,CAAC;QAClC,IAAI,EAAE,KAAK,GAAG,CAAC;QACf,KAAK,EAAE,cAAc,CAAC,QAAQ,GAAG,cAAc,CAAC,QAAQ;KACzD,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,oBAAoB,EAAE,CAAC;IAC9C,OAAO,CAAC,kBAAkB,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;IAC7D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACxC,MAAM,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,KAAK,EAAE,CAAC;IAChB,OAAO,CAAC,OAAO,EAAE,CAAC;IAClB,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DeviceInfo } from '../types.ts';
|
|
2
|
+
export interface GpuContext {
|
|
3
|
+
adapter: GPUAdapter;
|
|
4
|
+
device: GPUDevice;
|
|
5
|
+
info: DeviceInfo;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Acquires a GPUAdapter/GPUDevice directly from the browser's `navigator.gpu`
|
|
9
|
+
* (high-performance power preference), requesting every optional device
|
|
10
|
+
* feature this suite can make use of — f16 shaders and GPU timestamp
|
|
11
|
+
* queries — without failing if the device lacks some of them. Each benchmark
|
|
12
|
+
* checks `info.supports*` and reports itself as "skipped" rather than
|
|
13
|
+
* crashing the suite when a feature is missing.
|
|
14
|
+
*/
|
|
15
|
+
export declare function acquireGpuContext(): Promise<GpuContext>;
|
|
16
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/gpu/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,UAAU,CAAC;CAClB;AAWD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,UAAU,CAAC,CA4D7D"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const OPTIONAL_FEATURES = ['shader-f16', 'timestamp-query'];
|
|
2
|
+
// `packed_4x8_integer_dot_product` is a WGSL *language* extension (enabled in
|
|
3
|
+
// shader source via `enable packed_4x8_integer_dot_product;`), not a
|
|
4
|
+
// GPUFeatureName — it's never granted via requestDevice()'s requiredFeatures
|
|
5
|
+
// and never appears in adapter.features / device.features. Support is
|
|
6
|
+
// reported separately through navigator.gpu.wgslLanguageFeatures.
|
|
7
|
+
const WGSL_I8_DOT_EXTENSION = 'packed_4x8_integer_dot_product';
|
|
8
|
+
/**
|
|
9
|
+
* Acquires a GPUAdapter/GPUDevice directly from the browser's `navigator.gpu`
|
|
10
|
+
* (high-performance power preference), requesting every optional device
|
|
11
|
+
* feature this suite can make use of — f16 shaders and GPU timestamp
|
|
12
|
+
* queries — without failing if the device lacks some of them. Each benchmark
|
|
13
|
+
* checks `info.supports*` and reports itself as "skipped" rather than
|
|
14
|
+
* crashing the suite when a feature is missing.
|
|
15
|
+
*/
|
|
16
|
+
export async function acquireGpuContext() {
|
|
17
|
+
if (!('gpu' in navigator) || !navigator.gpu) {
|
|
18
|
+
throw new Error('WebGPU is not available in this browser (navigator.gpu is undefined).');
|
|
19
|
+
}
|
|
20
|
+
const adapter = await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' });
|
|
21
|
+
if (!adapter) {
|
|
22
|
+
throw new Error('No suitable WebGPU adapter was found.');
|
|
23
|
+
}
|
|
24
|
+
const requiredFeatures = OPTIONAL_FEATURES.filter((f) => adapter.features.has(f));
|
|
25
|
+
const requiredLimits = {};
|
|
26
|
+
// Ask for the largest storage-buffer binding & workgroup budgets the adapter allows,
|
|
27
|
+
// so large matrices and tuned workgroup sizes aren't capped by conservative defaults.
|
|
28
|
+
for (const key of [
|
|
29
|
+
'maxStorageBufferBindingSize',
|
|
30
|
+
'maxBufferSize',
|
|
31
|
+
'maxComputeWorkgroupSizeX',
|
|
32
|
+
'maxComputeInvocationsPerWorkgroup',
|
|
33
|
+
'maxComputeWorkgroupStorageSize',
|
|
34
|
+
]) {
|
|
35
|
+
const value = adapter.limits[key];
|
|
36
|
+
if (typeof value === 'number')
|
|
37
|
+
requiredLimits[key] = value;
|
|
38
|
+
}
|
|
39
|
+
const device = await adapter.requestDevice({ requiredFeatures, requiredLimits });
|
|
40
|
+
const limits = {};
|
|
41
|
+
for (const key of Object.keys(device.limits)) {
|
|
42
|
+
const value = device.limits[key];
|
|
43
|
+
if (typeof value === 'number')
|
|
44
|
+
limits[key] = value;
|
|
45
|
+
}
|
|
46
|
+
let adapterInfoDescription;
|
|
47
|
+
let vendor;
|
|
48
|
+
let architecture;
|
|
49
|
+
const anyAdapter = adapter;
|
|
50
|
+
if (anyAdapter.info) {
|
|
51
|
+
vendor = anyAdapter.info.vendor || undefined;
|
|
52
|
+
architecture = anyAdapter.info.architecture || undefined;
|
|
53
|
+
adapterInfoDescription = anyAdapter.info.description || undefined;
|
|
54
|
+
}
|
|
55
|
+
const info = {
|
|
56
|
+
vendor,
|
|
57
|
+
architecture,
|
|
58
|
+
description: adapterInfoDescription,
|
|
59
|
+
features: [...device.features].toSorted(),
|
|
60
|
+
limits,
|
|
61
|
+
supportsF16: device.features.has('shader-f16'),
|
|
62
|
+
supportsI8Dot: Boolean(navigator.gpu.wgslLanguageFeatures?.has(WGSL_I8_DOT_EXTENSION)),
|
|
63
|
+
supportsTimestampQuery: device.features.has('timestamp-query'),
|
|
64
|
+
};
|
|
65
|
+
device.addEventListener('uncapturederror', (event) => {
|
|
66
|
+
// eslint-disable-next-line no-console
|
|
67
|
+
console.error('WebGPU uncaptured error:', event.error);
|
|
68
|
+
});
|
|
69
|
+
return { adapter, device, info };
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/gpu/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps a single compute pass in GPU timestamp queries when the device
|
|
3
|
+
* supports `timestamp-query`, giving pure device-side elapsed time (no CPU
|
|
4
|
+
* dispatch/driver overhead in the number). Falls back to CPU wall-clock
|
|
5
|
+
* around `queue.onSubmittedWorkDone()` otherwise — still a fair comparison
|
|
6
|
+
* across kernels, just with a little more noise.
|
|
7
|
+
*/
|
|
8
|
+
export declare class GpuTimer {
|
|
9
|
+
readonly supported: boolean;
|
|
10
|
+
private readonly querySet;
|
|
11
|
+
private readonly resolveBuffer;
|
|
12
|
+
private readonly resultBuffer;
|
|
13
|
+
constructor(device: GPUDevice, supported: boolean);
|
|
14
|
+
get timestampWrites(): GPUComputePassTimestampWrites | undefined;
|
|
15
|
+
resolve(encoder: GPUCommandEncoder): void;
|
|
16
|
+
/** Reads back the two timestamps written by the most recently submitted pass and returns elapsed ms. */
|
|
17
|
+
readElapsedMs(): Promise<number>;
|
|
18
|
+
destroy(): void;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=timing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../src/gpu/timing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,qBAAa,QAAQ;IAOjB,QAAQ,CAAC,SAAS,EAAE,OAAO;IAN7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IACrD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;IACxD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA0B;gBAGrD,MAAM,EAAE,SAAS,EACR,SAAS,EAAE,OAAO;IAe7B,IAAI,eAAe,IAAI,6BAA6B,GAAG,SAAS,CAG/D;IAED,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAMzC,wGAAwG;IAClG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAUtC,OAAO,IAAI,IAAI;CAKhB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps a single compute pass in GPU timestamp queries when the device
|
|
3
|
+
* supports `timestamp-query`, giving pure device-side elapsed time (no CPU
|
|
4
|
+
* dispatch/driver overhead in the number). Falls back to CPU wall-clock
|
|
5
|
+
* around `queue.onSubmittedWorkDone()` otherwise — still a fair comparison
|
|
6
|
+
* across kernels, just with a little more noise.
|
|
7
|
+
*/
|
|
8
|
+
export class GpuTimer {
|
|
9
|
+
supported;
|
|
10
|
+
querySet = null;
|
|
11
|
+
resolveBuffer = null;
|
|
12
|
+
resultBuffer = null;
|
|
13
|
+
constructor(device, supported) {
|
|
14
|
+
this.supported = supported;
|
|
15
|
+
if (supported) {
|
|
16
|
+
this.querySet = device.createQuerySet({ type: 'timestamp', count: 2 });
|
|
17
|
+
this.resolveBuffer = device.createBuffer({
|
|
18
|
+
size: 16,
|
|
19
|
+
usage: GPUBufferUsage.QUERY_RESOLVE | GPUBufferUsage.COPY_SRC,
|
|
20
|
+
});
|
|
21
|
+
this.resultBuffer = device.createBuffer({
|
|
22
|
+
size: 16,
|
|
23
|
+
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
get timestampWrites() {
|
|
28
|
+
if (!this.supported || !this.querySet)
|
|
29
|
+
return undefined;
|
|
30
|
+
return { querySet: this.querySet, beginningOfPassWriteIndex: 0, endOfPassWriteIndex: 1 };
|
|
31
|
+
}
|
|
32
|
+
resolve(encoder) {
|
|
33
|
+
if (!this.supported || !this.querySet || !this.resolveBuffer || !this.resultBuffer)
|
|
34
|
+
return;
|
|
35
|
+
encoder.resolveQuerySet(this.querySet, 0, 2, this.resolveBuffer, 0);
|
|
36
|
+
encoder.copyBufferToBuffer(this.resolveBuffer, 0, this.resultBuffer, 0, 16);
|
|
37
|
+
}
|
|
38
|
+
/** Reads back the two timestamps written by the most recently submitted pass and returns elapsed ms. */
|
|
39
|
+
async readElapsedMs() {
|
|
40
|
+
if (!this.supported || !this.resultBuffer)
|
|
41
|
+
return Number.NaN;
|
|
42
|
+
await this.resultBuffer.mapAsync(GPUMapMode.READ);
|
|
43
|
+
const times = new BigInt64Array(this.resultBuffer.getMappedRange().slice(0));
|
|
44
|
+
this.resultBuffer.unmap();
|
|
45
|
+
const start = times[0];
|
|
46
|
+
const end = times[1];
|
|
47
|
+
return Number(end - start) / 1e6; // nanoseconds -> milliseconds
|
|
48
|
+
}
|
|
49
|
+
destroy() {
|
|
50
|
+
this.querySet?.destroy();
|
|
51
|
+
this.resolveBuffer?.destroy();
|
|
52
|
+
this.resultBuffer?.destroy();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=timing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.js","sourceRoot":"","sources":["../../src/gpu/timing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,OAAO,QAAQ;IAOR;IANM,QAAQ,GAAuB,IAAI,CAAC;IACpC,aAAa,GAAqB,IAAI,CAAC;IACvC,YAAY,GAAqB,IAAI,CAAC;IAEvD,YACE,MAAiB,EACR,SAAkB;QAAlB,cAAS,GAAT,SAAS,CAAS;QAE3B,IAAI,SAAS,EAAE,CAAC;YACd,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YACvE,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,YAAY,CAAC;gBACvC,IAAI,EAAE,EAAE;gBACR,KAAK,EAAE,cAAc,CAAC,aAAa,GAAG,cAAc,CAAC,QAAQ;aAC9D,CAAC,CAAC;YACH,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;gBACtC,IAAI,EAAE,EAAE;gBACR,KAAK,EAAE,cAAc,CAAC,QAAQ,GAAG,cAAc,CAAC,QAAQ;aACzD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,eAAe;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ;YAAE,OAAO,SAAS,CAAC;QACxD,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,yBAAyB,EAAE,CAAC,EAAE,mBAAmB,EAAE,CAAC,EAAE,CAAC;IAC3F,CAAC;IAED,OAAO,CAAC,OAA0B;QAChC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO;QAC3F,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,wGAAwG;IACxG,KAAK,CAAC,aAAa;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE,OAAO,MAAM,CAAC,GAAG,CAAC;QAC7D,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7E,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACxB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACtB,OAAO,MAAM,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,8BAA8B;IAClE,CAAC;IAED,OAAO;QACL,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;QACzB,IAAI,CAAC,aAAa,EAAE,OAAO,EAAE,CAAC;QAC9B,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,CAAC;IAC/B,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { runSuite } from './suite.ts';
|
|
2
|
+
export type { BenchmarkResult, SuiteOptions, SuiteProgressEvent, DeviceInfo, Stats, BenchmarkCategory, BenchmarkStatus, TimingMethod, SamplingStopReason, } from './types.ts';
|
|
3
|
+
export { computeStats, isBestStable, isThrottled, tQuantile975 } from './stats.ts';
|
|
4
|
+
export type { BestStableOptions } from './stats.ts';
|
|
5
|
+
export { DEFAULT_SAMPLING, runSampling, recordSample, roundIsThrottled, resolveSamplingConfig } from './sampling.ts';
|
|
6
|
+
export type { SamplingConfig, Sampleable, SampleState, SampleStateSnapshot, RunSamplingOptions } from './sampling.ts';
|
|
7
|
+
export { DEFAULT_MEASUREMENT, KernelSampler } from './gpu/benchmarkRunner.ts';
|
|
8
|
+
export type { MeasurementConfig, KernelHarness, WorkKnob } from './gpu/benchmarkRunner.ts';
|
|
9
|
+
export { generateMatVecData, padToMultipleOf4, mulberry32 } from './data/generate.ts';
|
|
10
|
+
export { acquireGpuContext } from './gpu/context.ts';
|
|
11
|
+
export type { GpuContext } from './gpu/context.ts';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EACV,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,UAAU,EACV,KAAK,EACL,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,kBAAkB,GACnB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACnF,YAAY,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACrH,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACtH,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9E,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAC3F,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { runSuite } from "./suite.js";
|
|
2
|
+
export { computeStats, isBestStable, isThrottled, tQuantile975 } from "./stats.js";
|
|
3
|
+
export { DEFAULT_SAMPLING, runSampling, recordSample, roundIsThrottled, resolveSamplingConfig } from "./sampling.js";
|
|
4
|
+
export { DEFAULT_MEASUREMENT, KernelSampler } from "./gpu/benchmarkRunner.js";
|
|
5
|
+
export { generateMatVecData, padToMultipleOf4, mulberry32 } from "./data/generate.js";
|
|
6
|
+
export { acquireGpuContext } from "./gpu/context.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAYtC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEnF,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,YAAY,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAErH,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9E,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACtF,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
|