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.
Files changed (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -0
  3. package/dist/benchmarks/common.d.ts +71 -0
  4. package/dist/benchmarks/common.d.ts.map +1 -0
  5. package/dist/benchmarks/common.js +104 -0
  6. package/dist/benchmarks/common.js.map +1 -0
  7. package/dist/benchmarks/flopsCommon.d.ts +52 -0
  8. package/dist/benchmarks/flopsCommon.d.ts.map +1 -0
  9. package/dist/benchmarks/flopsCommon.js +78 -0
  10. package/dist/benchmarks/flopsCommon.js.map +1 -0
  11. package/dist/benchmarks/flopsConvert.d.ts +18 -0
  12. package/dist/benchmarks/flopsConvert.d.ts.map +1 -0
  13. package/dist/benchmarks/flopsConvert.js +61 -0
  14. package/dist/benchmarks/flopsConvert.js.map +1 -0
  15. package/dist/benchmarks/flopsF16.d.ts +12 -0
  16. package/dist/benchmarks/flopsF16.d.ts.map +1 -0
  17. package/dist/benchmarks/flopsF16.js +54 -0
  18. package/dist/benchmarks/flopsF16.js.map +1 -0
  19. package/dist/benchmarks/flopsF32.d.ts +12 -0
  20. package/dist/benchmarks/flopsF32.d.ts.map +1 -0
  21. package/dist/benchmarks/flopsF32.js +50 -0
  22. package/dist/benchmarks/flopsF32.js.map +1 -0
  23. package/dist/benchmarks/flopsI8.d.ts +16 -0
  24. package/dist/benchmarks/flopsI8.d.ts.map +1 -0
  25. package/dist/benchmarks/flopsI8.js +83 -0
  26. package/dist/benchmarks/flopsI8.js.map +1 -0
  27. package/dist/benchmarks/flopsMath.d.ts +28 -0
  28. package/dist/benchmarks/flopsMath.d.ts.map +1 -0
  29. package/dist/benchmarks/flopsMath.js +98 -0
  30. package/dist/benchmarks/flopsMath.js.map +1 -0
  31. package/dist/benchmarks/streamBandwidth.d.ts +17 -0
  32. package/dist/benchmarks/streamBandwidth.d.ts.map +1 -0
  33. package/dist/benchmarks/streamBandwidth.js +78 -0
  34. package/dist/benchmarks/streamBandwidth.js.map +1 -0
  35. package/dist/data/generate.d.ts +23 -0
  36. package/dist/data/generate.d.ts.map +1 -0
  37. package/dist/data/generate.js +41 -0
  38. package/dist/data/generate.js.map +1 -0
  39. package/dist/gpu/benchmarkRunner.d.ts +98 -0
  40. package/dist/gpu/benchmarkRunner.d.ts.map +1 -0
  41. package/dist/gpu/benchmarkRunner.js +178 -0
  42. package/dist/gpu/benchmarkRunner.js.map +1 -0
  43. package/dist/gpu/buffers.d.ts +8 -0
  44. package/dist/gpu/buffers.d.ts.map +1 -0
  45. package/dist/gpu/buffers.js +41 -0
  46. package/dist/gpu/buffers.js.map +1 -0
  47. package/dist/gpu/context.d.ts +16 -0
  48. package/dist/gpu/context.d.ts.map +1 -0
  49. package/dist/gpu/context.js +71 -0
  50. package/dist/gpu/context.js.map +1 -0
  51. package/dist/gpu/timing.d.ts +20 -0
  52. package/dist/gpu/timing.d.ts.map +1 -0
  53. package/dist/gpu/timing.js +55 -0
  54. package/dist/gpu/timing.js.map +1 -0
  55. package/dist/index.d.ts +12 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +7 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/sampling.d.ts +96 -0
  60. package/dist/sampling.d.ts.map +1 -0
  61. package/dist/sampling.js +239 -0
  62. package/dist/sampling.js.map +1 -0
  63. package/dist/shaders/flopsF16Mat4.d.ts +7 -0
  64. package/dist/shaders/flopsF16Mat4.d.ts.map +1 -0
  65. package/dist/shaders/flopsF16Mat4.js +40 -0
  66. package/dist/shaders/flopsF16Mat4.js.map +1 -0
  67. package/dist/shaders/flopsF16Matvec.d.ts +14 -0
  68. package/dist/shaders/flopsF16Matvec.d.ts.map +1 -0
  69. package/dist/shaders/flopsF16Matvec.js +58 -0
  70. package/dist/shaders/flopsF16Matvec.js.map +1 -0
  71. package/dist/shaders/flopsF16Scalar.d.ts +8 -0
  72. package/dist/shaders/flopsF16Scalar.d.ts.map +1 -0
  73. package/dist/shaders/flopsF16Scalar.js +71 -0
  74. package/dist/shaders/flopsF16Scalar.js.map +1 -0
  75. package/dist/shaders/flopsF16Vec4.d.ts +7 -0
  76. package/dist/shaders/flopsF16Vec4.d.ts.map +1 -0
  77. package/dist/shaders/flopsF16Vec4.js +35 -0
  78. package/dist/shaders/flopsF16Vec4.js.map +1 -0
  79. package/dist/shaders/flopsF32Div.d.ts +10 -0
  80. package/dist/shaders/flopsF32Div.d.ts.map +1 -0
  81. package/dist/shaders/flopsF32Div.js +71 -0
  82. package/dist/shaders/flopsF32Div.js.map +1 -0
  83. package/dist/shaders/flopsF32F16Convert.d.ts +11 -0
  84. package/dist/shaders/flopsF32F16Convert.d.ts.map +1 -0
  85. package/dist/shaders/flopsF32F16Convert.js +73 -0
  86. package/dist/shaders/flopsF32F16Convert.js.map +1 -0
  87. package/dist/shaders/flopsF32Log.d.ts +11 -0
  88. package/dist/shaders/flopsF32Log.d.ts.map +1 -0
  89. package/dist/shaders/flopsF32Log.js +71 -0
  90. package/dist/shaders/flopsF32Log.js.map +1 -0
  91. package/dist/shaders/flopsF32Mat4.d.ts +9 -0
  92. package/dist/shaders/flopsF32Mat4.d.ts.map +1 -0
  93. package/dist/shaders/flopsF32Mat4.js +38 -0
  94. package/dist/shaders/flopsF32Mat4.js.map +1 -0
  95. package/dist/shaders/flopsF32Matvec.d.ts +14 -0
  96. package/dist/shaders/flopsF32Matvec.d.ts.map +1 -0
  97. package/dist/shaders/flopsF32Matvec.js +58 -0
  98. package/dist/shaders/flopsF32Matvec.js.map +1 -0
  99. package/dist/shaders/flopsF32Pow.d.ts +10 -0
  100. package/dist/shaders/flopsF32Pow.d.ts.map +1 -0
  101. package/dist/shaders/flopsF32Pow.js +71 -0
  102. package/dist/shaders/flopsF32Pow.js.map +1 -0
  103. package/dist/shaders/flopsF32Rsqrt.d.ts +12 -0
  104. package/dist/shaders/flopsF32Rsqrt.d.ts.map +1 -0
  105. package/dist/shaders/flopsF32Rsqrt.js +72 -0
  106. package/dist/shaders/flopsF32Rsqrt.js.map +1 -0
  107. package/dist/shaders/flopsF32Scalar.d.ts +11 -0
  108. package/dist/shaders/flopsF32Scalar.d.ts.map +1 -0
  109. package/dist/shaders/flopsF32Scalar.js +72 -0
  110. package/dist/shaders/flopsF32Scalar.js.map +1 -0
  111. package/dist/shaders/flopsF32Sincos.d.ts +9 -0
  112. package/dist/shaders/flopsF32Sincos.d.ts.map +1 -0
  113. package/dist/shaders/flopsF32Sincos.js +68 -0
  114. package/dist/shaders/flopsF32Sincos.js.map +1 -0
  115. package/dist/shaders/flopsF32Sqrt.d.ts +9 -0
  116. package/dist/shaders/flopsF32Sqrt.d.ts.map +1 -0
  117. package/dist/shaders/flopsF32Sqrt.js +69 -0
  118. package/dist/shaders/flopsF32Sqrt.js.map +1 -0
  119. package/dist/shaders/flopsF32Vec4.d.ts +9 -0
  120. package/dist/shaders/flopsF32Vec4.d.ts.map +1 -0
  121. package/dist/shaders/flopsF32Vec4.js +33 -0
  122. package/dist/shaders/flopsF32Vec4.js.map +1 -0
  123. package/dist/shaders/flopsI32Div.d.ts +10 -0
  124. package/dist/shaders/flopsI32Div.d.ts.map +1 -0
  125. package/dist/shaders/flopsI32Div.js +71 -0
  126. package/dist/shaders/flopsI32Div.js.map +1 -0
  127. package/dist/shaders/flopsI32F16Convert.d.ts +10 -0
  128. package/dist/shaders/flopsI32F16Convert.d.ts.map +1 -0
  129. package/dist/shaders/flopsI32F16Convert.js +71 -0
  130. package/dist/shaders/flopsI32F16Convert.js.map +1 -0
  131. package/dist/shaders/flopsI32F32Convert.d.ts +10 -0
  132. package/dist/shaders/flopsI32F32Convert.d.ts.map +1 -0
  133. package/dist/shaders/flopsI32F32Convert.js +71 -0
  134. package/dist/shaders/flopsI32F32Convert.js.map +1 -0
  135. package/dist/shaders/flopsI8Dp4a.d.ts +23 -0
  136. package/dist/shaders/flopsI8Dp4a.d.ts.map +1 -0
  137. package/dist/shaders/flopsI8Dp4a.js +49 -0
  138. package/dist/shaders/flopsI8Dp4a.js.map +1 -0
  139. package/dist/shaders/flopsI8Mat4.d.ts +9 -0
  140. package/dist/shaders/flopsI8Mat4.d.ts.map +1 -0
  141. package/dist/shaders/flopsI8Mat4.js +41 -0
  142. package/dist/shaders/flopsI8Mat4.js.map +1 -0
  143. package/dist/shaders/flopsI8Matvec.d.ts +16 -0
  144. package/dist/shaders/flopsI8Matvec.d.ts.map +1 -0
  145. package/dist/shaders/flopsI8Matvec.js +57 -0
  146. package/dist/shaders/flopsI8Matvec.js.map +1 -0
  147. package/dist/shaders/flopsI8MatvecDp4a.d.ts +17 -0
  148. package/dist/shaders/flopsI8MatvecDp4a.d.ts.map +1 -0
  149. package/dist/shaders/flopsI8MatvecDp4a.js +60 -0
  150. package/dist/shaders/flopsI8MatvecDp4a.js.map +1 -0
  151. package/dist/shaders/flopsI8Scalar.d.ts +9 -0
  152. package/dist/shaders/flopsI8Scalar.d.ts.map +1 -0
  153. package/dist/shaders/flopsI8Scalar.js +70 -0
  154. package/dist/shaders/flopsI8Scalar.js.map +1 -0
  155. package/dist/shaders/flopsI8Vec4.d.ts +7 -0
  156. package/dist/shaders/flopsI8Vec4.d.ts.map +1 -0
  157. package/dist/shaders/flopsI8Vec4.js +31 -0
  158. package/dist/shaders/flopsI8Vec4.js.map +1 -0
  159. package/dist/shaders/flopsU32PackUnpack.d.ts +11 -0
  160. package/dist/shaders/flopsU32PackUnpack.d.ts.map +1 -0
  161. package/dist/shaders/flopsU32PackUnpack.js +55 -0
  162. package/dist/shaders/flopsU32PackUnpack.js.map +1 -0
  163. package/dist/shaders/streamRead.d.ts +10 -0
  164. package/dist/shaders/streamRead.d.ts.map +1 -0
  165. package/dist/shaders/streamRead.js +33 -0
  166. package/dist/shaders/streamRead.js.map +1 -0
  167. package/dist/shaders/streamWrite.d.ts +8 -0
  168. package/dist/shaders/streamWrite.d.ts.map +1 -0
  169. package/dist/shaders/streamWrite.js +29 -0
  170. package/dist/shaders/streamWrite.js.map +1 -0
  171. package/dist/stats.d.ts +31 -0
  172. package/dist/stats.d.ts.map +1 -0
  173. package/dist/stats.js +67 -0
  174. package/dist/stats.js.map +1 -0
  175. package/dist/suite.d.ts +25 -0
  176. package/dist/suite.d.ts.map +1 -0
  177. package/dist/suite.js +196 -0
  178. package/dist/suite.js.map +1 -0
  179. package/dist/types.d.ts +171 -0
  180. package/dist/types.d.ts.map +1 -0
  181. package/dist/types.js +2 -0
  182. package/dist/types.js.map +1 -0
  183. package/package.json +45 -0
@@ -0,0 +1,96 @@
1
+ import type { SamplingStopReason, Stats, SuiteProgressEvent } from './types.ts';
2
+ /** Knobs for the round-robin, best-of-N sampling scheduler. */
3
+ export interface SamplingConfig {
4
+ /** Fewest kept measurements per benchmark before it can converge. Default 3. */
5
+ minRounds?: number;
6
+ /** Most kept measurements per benchmark. Default 10. */
7
+ maxRounds?: number;
8
+ /** Converged once the best run hasn't improved (by more than `improvementTolerance`) over this many kept measurements. Default 2. */
9
+ stableRounds?: number;
10
+ /** Relative improvement that counts as "still improving". Default 0.01. */
11
+ improvementTolerance?: number;
12
+ /** A measurement this fraction slower than the best is throttled and discarded. Default 0.20. */
13
+ throttleThreshold?: number;
14
+ /** Idle gap between consecutive measurements, in ms. Default 100. */
15
+ idleMs?: number;
16
+ /** Suite-wide pause when throttling is detected, in ms. Default 3000. */
17
+ cooldownMs?: number;
18
+ /** Cooldown pauses before giving up on still-unconverged benchmarks. Default 3. */
19
+ maxCooldowns?: number;
20
+ /** Fraction of a round's measurements (and at least two) that must be throttled to trigger a cooldown. Default 0.5. */
21
+ throttledFraction?: number;
22
+ }
23
+ export declare const DEFAULT_SAMPLING: Required<SamplingConfig>;
24
+ /** What the scheduler needs from a benchmark: something it can measure repeatedly. */
25
+ export interface Sampleable {
26
+ id: string;
27
+ /** One-time setup (calibration, warmups). Called before the first `sample()`. */
28
+ calibrate(): Promise<void>;
29
+ /** One timed measurement: per-op time in ms. */
30
+ sample(): Promise<number>;
31
+ }
32
+ /** Running state of one benchmark inside the scheduler; surfaced after every measurement via `onUpdate`. */
33
+ export interface SampleState {
34
+ id: string;
35
+ /** Kept measurements, in order taken. */
36
+ timesMs: number[];
37
+ /** Measurements discarded as throttled, in order taken. */
38
+ throttledMs: number[];
39
+ /** Best kept measurement so far; Infinity before the first. */
40
+ bestMs: number;
41
+ stopReason?: SamplingStopReason;
42
+ /** Set if calibration or a measurement threw; the benchmark is retired. */
43
+ error?: unknown;
44
+ }
45
+ export interface SampleStateSnapshot extends SampleState {
46
+ stats?: Stats;
47
+ }
48
+ export interface RunSamplingOptions extends SamplingConfig {
49
+ /** Called after every measurement (and when a benchmark is retired) with that benchmark's state. */
50
+ onUpdate?: (state: SampleStateSnapshot) => void | Promise<void>;
51
+ onProgress?: (event: SuiteProgressEvent) => void;
52
+ /** Injectable for tests; defaults to a real `setTimeout` sleep. */
53
+ sleep?: (ms: number) => Promise<void>;
54
+ }
55
+ /**
56
+ * Classifies one measurement against the benchmark's best-so-far and folds it
57
+ * into the state. Pure apart from mutating `state`.
58
+ *
59
+ * Returns whether the sample was kept. A throttled sample (more than
60
+ * `throttleThreshold` slower than the best) is recorded in `throttledMs` and
61
+ * doesn't count toward convergence — it says something about the device's
62
+ * temperature, not the kernel.
63
+ */
64
+ export declare function recordSample(state: SampleState, ms: number, cfg: Required<SamplingConfig>): boolean;
65
+ /**
66
+ * Whether the measurements taken in one round add up to "the device is
67
+ * throttled". Thermal throttling slows every kernel, so the signal is
68
+ * agreement across benchmarks: at least two of them, and at least
69
+ * `throttledFraction` of the round. One benchmark repeatedly coming in slow
70
+ * while the rest are fine is that benchmark's own noise (Safari's
71
+ * wall-clock timing of the write-bandwidth kernel jitters 15–40%), not
72
+ * heat — pausing the suite for it would just waste time.
73
+ */
74
+ export declare function roundIsThrottled(roundThrottled: readonly boolean[], cfg: Required<SamplingConfig>): boolean;
75
+ /**
76
+ * Round-robin, best-of-N sampling across many benchmarks.
77
+ *
78
+ * Rather than running each benchmark to convergence before starting the
79
+ * next (which heats a phone up in seconds and leaves every later benchmark
80
+ * measuring a throttled GPU), every round takes one short measurement of
81
+ * every still-active benchmark, with an idle gap between measurements so
82
+ * the GPU duty-cycles. Benchmarks retire as soon as their best run has
83
+ * stopped improving (`isBestStable`). Measurements that come in well slower
84
+ * than a benchmark's best are discarded as throttled; when a round shows
85
+ * the device throttled overall — half the benchmarks slow, or any one slow
86
+ * twice running — the whole suite pauses for `cooldownMs` and tries again,
87
+ * up to `maxCooldowns` times, after which whatever is still unconverged is
88
+ * reported with `stopReason: 'throttled'` alongside its best run so far.
89
+ *
90
+ * Only the best run is the headline number: every noise source makes runs
91
+ * slower, never faster, so the minimum is the most robust estimate of what
92
+ * the device can do.
93
+ */
94
+ export declare function runSampling(benchmarks: readonly Sampleable[], options?: RunSamplingOptions): Promise<Map<string, SampleStateSnapshot>>;
95
+ export declare function resolveSamplingConfig(cfg: SamplingConfig): Required<SamplingConfig>;
96
+ //# sourceMappingURL=sampling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampling.d.ts","sourceRoot":"","sources":["../src/sampling.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhF,+DAA+D;AAC/D,MAAM,WAAW,cAAc;IAC7B,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qIAAqI;IACrI,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mFAAmF;IACnF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uHAAuH;IACvH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,cAAc,CAUrD,CAAC;AAEF,sFAAsF;AACtF,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,iFAAiF;IACjF,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,gDAAgD;IAChD,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3B;AAED,4GAA4G;AAC5G,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,yCAAyC;IACzC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,2EAA2E;IAC3E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,oGAAoG;IACpG,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACjD,mEAAmE;IACnE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAmDD;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,OAAO,CAsBnG;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,SAAS,OAAO,EAAE,EAAE,GAAG,EAAE,QAAQ,CAAC,cAAc,CAAC,GAAG,OAAO,CAG3G;AAoBD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,WAAW,CAC/B,UAAU,EAAE,SAAS,UAAU,EAAE,EACjC,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC,CAqF3C;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,cAAc,GAAG,QAAQ,CAAC,cAAc,CAAC,CAanF"}
@@ -0,0 +1,239 @@
1
+ import { computeStats, isBestStable, isThrottled } from "./stats.js";
2
+ export const DEFAULT_SAMPLING = {
3
+ minRounds: 3,
4
+ maxRounds: 10,
5
+ stableRounds: 2,
6
+ improvementTolerance: 0.01,
7
+ throttleThreshold: 0.2,
8
+ idleMs: 100,
9
+ cooldownMs: 3000,
10
+ maxCooldowns: 3,
11
+ throttledFraction: 0.5,
12
+ };
13
+ const isActive = (s) => s.stopReason === undefined && s.error === undefined;
14
+ /**
15
+ * The idle gap doubles as the UI's render window: after the timer, wait for
16
+ * the main thread to go idle (table re-render committed, its paint queued)
17
+ * so the compositor's GPU work lands here rather than inside the next
18
+ * measurement. Falls back to a plain timer where `requestIdleCallback` is
19
+ * missing; the timeout bounds the wait on a busy page.
20
+ */
21
+ const realSleep = (ms) => new Promise((resolve) => setTimeout(() => {
22
+ if (typeof requestIdleCallback === 'function')
23
+ requestIdleCallback(() => resolve(), { timeout: 1000 });
24
+ else
25
+ resolve();
26
+ }, ms));
27
+ /**
28
+ * A backgrounded tab gets a throttled event loop and a lower-priority GPU
29
+ * queue, so nothing measured while hidden is a property of the device.
30
+ * `waitVisible` blocks until the page is showing; `hiddenSince` reports
31
+ * whether it went hidden at any point after the last `mark()`.
32
+ */
33
+ function visibilityGuard() {
34
+ const doc = typeof document === 'undefined' ? undefined : document;
35
+ let hidden = false;
36
+ const onChange = () => {
37
+ if (doc?.hidden)
38
+ hidden = true;
39
+ };
40
+ doc?.addEventListener('visibilitychange', onChange);
41
+ return {
42
+ mark: () => {
43
+ hidden = Boolean(doc?.hidden);
44
+ },
45
+ hiddenSince: () => hidden || Boolean(doc?.hidden),
46
+ waitVisible: () => new Promise((resolve) => {
47
+ if (!doc?.hidden)
48
+ return resolve();
49
+ const onVisible = () => {
50
+ if (doc.hidden)
51
+ return;
52
+ doc.removeEventListener('visibilitychange', onVisible);
53
+ resolve();
54
+ };
55
+ doc.addEventListener('visibilitychange', onVisible);
56
+ }),
57
+ dispose: () => doc?.removeEventListener('visibilitychange', onChange),
58
+ };
59
+ }
60
+ /**
61
+ * Classifies one measurement against the benchmark's best-so-far and folds it
62
+ * into the state. Pure apart from mutating `state`.
63
+ *
64
+ * Returns whether the sample was kept. A throttled sample (more than
65
+ * `throttleThreshold` slower than the best) is recorded in `throttledMs` and
66
+ * doesn't count toward convergence — it says something about the device's
67
+ * temperature, not the kernel.
68
+ */
69
+ export function recordSample(state, ms, cfg) {
70
+ if (isThrottled(ms, state.bestMs, cfg.throttleThreshold)) {
71
+ state.throttledMs.push(ms);
72
+ // A benchmark that keeps coming back throttled while the rest of the
73
+ // suite is fine (so no cooldown fires) must still terminate.
74
+ if (state.timesMs.length + state.throttledMs.length >= 2 * cfg.maxRounds)
75
+ state.stopReason = 'max-rounds';
76
+ return false;
77
+ }
78
+ state.timesMs.push(ms);
79
+ if (ms < state.bestMs)
80
+ state.bestMs = ms;
81
+ if (isBestStable(state.timesMs, {
82
+ minRuns: cfg.minRounds,
83
+ stableRuns: cfg.stableRounds,
84
+ tolerance: cfg.improvementTolerance,
85
+ })) {
86
+ state.stopReason = 'converged';
87
+ }
88
+ else if (state.timesMs.length >= cfg.maxRounds) {
89
+ state.stopReason = 'max-rounds';
90
+ }
91
+ return true;
92
+ }
93
+ /**
94
+ * Whether the measurements taken in one round add up to "the device is
95
+ * throttled". Thermal throttling slows every kernel, so the signal is
96
+ * agreement across benchmarks: at least two of them, and at least
97
+ * `throttledFraction` of the round. One benchmark repeatedly coming in slow
98
+ * while the rest are fine is that benchmark's own noise (Safari's
99
+ * wall-clock timing of the write-bandwidth kernel jitters 15–40%), not
100
+ * heat — pausing the suite for it would just waste time.
101
+ */
102
+ export function roundIsThrottled(roundThrottled, cfg) {
103
+ const throttledCount = roundThrottled.filter(Boolean).length;
104
+ return throttledCount >= 2 && throttledCount / roundThrottled.length >= cfg.throttledFraction;
105
+ }
106
+ /** In-place Fisher-Yates. */
107
+ function shuffle(items) {
108
+ for (let i = items.length - 1; i > 0; i--) {
109
+ const j = Math.floor(Math.random() * (i + 1));
110
+ [items[i], items[j]] = [items[j], items[i]];
111
+ }
112
+ return items;
113
+ }
114
+ function snapshot(state) {
115
+ return {
116
+ ...state,
117
+ timesMs: [...state.timesMs],
118
+ throttledMs: [...state.throttledMs],
119
+ stats: state.timesMs.length > 0 ? computeStats(state.timesMs) : undefined,
120
+ };
121
+ }
122
+ /**
123
+ * Round-robin, best-of-N sampling across many benchmarks.
124
+ *
125
+ * Rather than running each benchmark to convergence before starting the
126
+ * next (which heats a phone up in seconds and leaves every later benchmark
127
+ * measuring a throttled GPU), every round takes one short measurement of
128
+ * every still-active benchmark, with an idle gap between measurements so
129
+ * the GPU duty-cycles. Benchmarks retire as soon as their best run has
130
+ * stopped improving (`isBestStable`). Measurements that come in well slower
131
+ * than a benchmark's best are discarded as throttled; when a round shows
132
+ * the device throttled overall — half the benchmarks slow, or any one slow
133
+ * twice running — the whole suite pauses for `cooldownMs` and tries again,
134
+ * up to `maxCooldowns` times, after which whatever is still unconverged is
135
+ * reported with `stopReason: 'throttled'` alongside its best run so far.
136
+ *
137
+ * Only the best run is the headline number: every noise source makes runs
138
+ * slower, never faster, so the minimum is the most robust estimate of what
139
+ * the device can do.
140
+ */
141
+ export async function runSampling(benchmarks, options = {}) {
142
+ const cfg = resolveSamplingConfig(options);
143
+ const sleep = options.sleep ?? realSleep;
144
+ const onUpdate = options.onUpdate ?? (() => { });
145
+ const onProgress = options.onProgress ?? (() => { });
146
+ const states = new Map();
147
+ const samplers = new Map();
148
+ for (const b of benchmarks) {
149
+ states.set(b.id, { id: b.id, timesMs: [], throttledMs: [], bestMs: Number.POSITIVE_INFINITY });
150
+ samplers.set(b.id, b);
151
+ }
152
+ const visibility = visibilityGuard();
153
+ let cooldowns = 0;
154
+ let calibrated = false;
155
+ try {
156
+ for (let round = 1;; round++) {
157
+ // Fresh order every round: a fixed order would hand the same kernels
158
+ // the cool GPU every time and leave the rest measuring a warm one.
159
+ const active = shuffle([...states.values()].filter(isActive));
160
+ if (active.length === 0)
161
+ break;
162
+ onProgress({ type: 'round', round, active: active.length });
163
+ const roundStates = [];
164
+ const roundThrottled = [];
165
+ let first = true;
166
+ for (const state of active) {
167
+ const sampler = samplers.get(state.id);
168
+ try {
169
+ if (!first && cfg.idleMs > 0)
170
+ await sleep(cfg.idleMs);
171
+ first = false;
172
+ await visibility.waitVisible();
173
+ visibility.mark();
174
+ if (!calibrated)
175
+ await sampler.calibrate();
176
+ const ms = await sampler.sample();
177
+ // Hidden at any point during the measurement: not a device number. Drop it and retry next round.
178
+ if (visibility.hiddenSince())
179
+ continue;
180
+ if (!Number.isFinite(ms) || ms <= 0) {
181
+ throw new Error(`measured a per-op time of ${ms}ms — the GPU pass likely did no work (check for an 'uncapturederror' in the console).`);
182
+ }
183
+ const kept = recordSample(state, ms, cfg);
184
+ roundStates.push(state);
185
+ roundThrottled.push(!kept);
186
+ }
187
+ catch (error) {
188
+ state.error = error;
189
+ }
190
+ await onUpdate(snapshot(state));
191
+ }
192
+ calibrated = true;
193
+ if (roundStates.length > 0 && roundIsThrottled(roundThrottled, cfg)) {
194
+ const throttledIds = roundStates.filter((_, i) => roundThrottled[i]).map((s) => s.id);
195
+ if (cooldowns >= cfg.maxCooldowns) {
196
+ onProgress({ type: 'throttle-abort', throttledIds });
197
+ for (const s of states.values()) {
198
+ if (isActive(s)) {
199
+ s.stopReason = s.timesMs.length > 0 ? 'throttled' : undefined;
200
+ if (s.timesMs.length === 0) {
201
+ s.error = new Error('Every measurement was discarded as throttled and the cooldown budget is exhausted.');
202
+ }
203
+ await onUpdate(snapshot(s));
204
+ }
205
+ }
206
+ break;
207
+ }
208
+ cooldowns += 1;
209
+ onProgress({
210
+ type: 'cooldown',
211
+ attempt: cooldowns,
212
+ maxAttempts: cfg.maxCooldowns,
213
+ ms: cfg.cooldownMs,
214
+ throttledIds,
215
+ });
216
+ await sleep(cfg.cooldownMs);
217
+ }
218
+ }
219
+ }
220
+ finally {
221
+ visibility.dispose();
222
+ }
223
+ return new Map([...states].map(([id, s]) => [id, snapshot(s)]));
224
+ }
225
+ export function resolveSamplingConfig(cfg) {
226
+ const minRounds = Math.max(1, cfg.minRounds ?? DEFAULT_SAMPLING.minRounds);
227
+ return {
228
+ minRounds,
229
+ maxRounds: Math.max(minRounds, cfg.maxRounds ?? DEFAULT_SAMPLING.maxRounds),
230
+ stableRounds: Math.max(1, cfg.stableRounds ?? DEFAULT_SAMPLING.stableRounds),
231
+ improvementTolerance: cfg.improvementTolerance ?? DEFAULT_SAMPLING.improvementTolerance,
232
+ throttleThreshold: cfg.throttleThreshold ?? DEFAULT_SAMPLING.throttleThreshold,
233
+ idleMs: cfg.idleMs ?? DEFAULT_SAMPLING.idleMs,
234
+ cooldownMs: cfg.cooldownMs ?? DEFAULT_SAMPLING.cooldownMs,
235
+ maxCooldowns: cfg.maxCooldowns ?? DEFAULT_SAMPLING.maxCooldowns,
236
+ throttledFraction: cfg.throttledFraction ?? DEFAULT_SAMPLING.throttledFraction,
237
+ };
238
+ }
239
+ //# sourceMappingURL=sampling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sampling.js","sourceRoot":"","sources":["../src/sampling.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * fp16 mat4 FLOPS probe — {@link flopsF32Mat4Wgsl}'s bounded
3
+ * `x = m * x + c` recurrence, but `m`, `c`, and `x` are all `f16`, so the
4
+ * mat4x4 * vec4 multiply runs entirely in half precision.
5
+ */
6
+ export declare const flopsF16Mat4Wgsl = "\nenable f16;\n\nstruct Params {\n threads: u32,\n iterations: u32,\n};\n\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read_write> out: array<f32>;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let idx = gid.x;\n if (idx >= params.threads) {\n return;\n }\n let s: f16 = f16(idx & 15u) * f16(0.001) + f16(0.001);\n let one: f16 = f16(1.0);\n let three_s: f16 = f16(3.0) * s;\n let m: mat4x4<f16> = mat4x4<f16>(\n vec4<f16>(one - three_s, s, s, s),\n vec4<f16>(s, one - three_s, s, s),\n vec4<f16>(s, s, one - three_s, s),\n vec4<f16>(s, s, s, one - three_s),\n );\n let c: vec4<f16> = vec4<f16>(s, s, s, s);\n var x: vec4<f16> = vec4<f16>(f16(idx & 255u) * f16(0.0001));\n for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {\n x = m * x + c;\n }\n out[idx] = f32(x.x + x.y + x.z + x.w);\n}\n";
7
+ //# sourceMappingURL=flopsF16Mat4.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsF16Mat4.d.ts","sourceRoot":"","sources":["../../src/shaders/flopsF16Mat4.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,65BAiC5B,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * fp16 mat4 FLOPS probe — {@link flopsF32Mat4Wgsl}'s bounded
3
+ * `x = m * x + c` recurrence, but `m`, `c`, and `x` are all `f16`, so the
4
+ * mat4x4 * vec4 multiply runs entirely in half precision.
5
+ */
6
+ export const flopsF16Mat4Wgsl = /* wgsl */ `
7
+ enable f16;
8
+
9
+ struct Params {
10
+ threads: u32,
11
+ iterations: u32,
12
+ };
13
+
14
+ @group(0) @binding(0) var<uniform> params: Params;
15
+ @group(0) @binding(1) var<storage, read_write> out: array<f32>;
16
+
17
+ @compute @workgroup_size(64)
18
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
19
+ let idx = gid.x;
20
+ if (idx >= params.threads) {
21
+ return;
22
+ }
23
+ let s: f16 = f16(idx & 15u) * f16(0.001) + f16(0.001);
24
+ let one: f16 = f16(1.0);
25
+ let three_s: f16 = f16(3.0) * s;
26
+ let m: mat4x4<f16> = mat4x4<f16>(
27
+ vec4<f16>(one - three_s, s, s, s),
28
+ vec4<f16>(s, one - three_s, s, s),
29
+ vec4<f16>(s, s, one - three_s, s),
30
+ vec4<f16>(s, s, s, one - three_s),
31
+ );
32
+ let c: vec4<f16> = vec4<f16>(s, s, s, s);
33
+ var x: vec4<f16> = vec4<f16>(f16(idx & 255u) * f16(0.0001));
34
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
35
+ x = m * x + c;
36
+ }
37
+ out[idx] = f32(x.x + x.y + x.z + x.w);
38
+ }
39
+ `;
40
+ //# sourceMappingURL=flopsF16Mat4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsF16Mat4.js","sourceRoot":"","sources":["../../src/shaders/flopsF16Mat4.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiC1C,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Register-resident matvec tile, entirely in fp16: a 4-row x 8-column weight tile held in
3
+ * registers (8 x vec4<f16>, hashed from the thread id so every thread has a
4
+ * different tile), applied to an 8-wide input vector every iteration. Each
5
+ * output row is two 4-wide dot() calls; the 4 outputs then become the next
6
+ * 4 input lanes (the other 4 shift over), so the loop is genuinely
7
+ * loop-carried and can't be hoisted. This is the same dot-product-and-
8
+ * accumulate shape as a real matvec/GEMV inner loop, but with no storage
9
+ * buffer traffic at all: it isolates how fast this dtype's dot products run
10
+ * on the ALU once bandwidth is out of the picture. 32 MACs (64 FLOPs) per
11
+ * loop iteration.
12
+ */
13
+ export declare const flopsF16MatvecWgsl = "\nenable f16;\n\nstruct Params {\n threads: u32,\n iterations: u32,\n};\n\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read_write> out: array<f32>;\n\n// Same weight hash as the fp32 kernel, but producing vec4<f16>.\nfn weights(seed: u32) -> vec4<f16> {\n let h = seed * 0x9e3779b1u;\n let bytes = vec4<u32>(h & 0xffu, (h >> 8u) & 0xffu, (h >> 16u) & 0xffu, h >> 24u);\n return (vec4<f16>(bytes) - vec4<f16>(127.5)) * f16(1.0 / 4096.0);\n}\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let idx = gid.x;\n if (idx >= params.threads) {\n return;\n }\n let w00: vec4<f16> = weights(idx * 8u + 1u);\n let w01: vec4<f16> = weights(idx * 8u + 2u);\n let w10: vec4<f16> = weights(idx * 8u + 3u);\n let w11: vec4<f16> = weights(idx * 8u + 4u);\n let w20: vec4<f16> = weights(idx * 8u + 5u);\n let w21: vec4<f16> = weights(idx * 8u + 6u);\n let w30: vec4<f16> = weights(idx * 8u + 7u);\n let w31: vec4<f16> = weights(idx * 8u + 8u);\n var x0: vec4<f16> = vec4<f16>(f16(idx & 255u) * f16(0.001) + f16(0.1), f16(0.2), f16(0.3), f16(0.4));\n var x1: vec4<f16> = vec4<f16>(f16(idx & 255u) * f16(0.001) + f16(0.2), f16(0.4), f16(0.6), f16(0.8));\n for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {\n let o0 = dot(w00, x0) + dot(w01, x1);\n let o1 = dot(w10, x0) + dot(w11, x1);\n let o2 = dot(w20, x0) + dot(w21, x1);\n let o3 = dot(w30, x0) + dot(w31, x1);\n x0 = x1;\n x1 = vec4<f16>(o0, o1, o2, o3);\n }\n out[idx] = f32(x0.x + x0.y + x0.z + x0.w + x1.x + x1.y + x1.z + x1.w);\n}\n";
14
+ //# sourceMappingURL=flopsF16Matvec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsF16Matvec.d.ts","sourceRoot":"","sources":["../../src/shaders/flopsF16Matvec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,+kDA4C9B,CAAC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Register-resident matvec tile, entirely in fp16: a 4-row x 8-column weight tile held in
3
+ * registers (8 x vec4<f16>, hashed from the thread id so every thread has a
4
+ * different tile), applied to an 8-wide input vector every iteration. Each
5
+ * output row is two 4-wide dot() calls; the 4 outputs then become the next
6
+ * 4 input lanes (the other 4 shift over), so the loop is genuinely
7
+ * loop-carried and can't be hoisted. This is the same dot-product-and-
8
+ * accumulate shape as a real matvec/GEMV inner loop, but with no storage
9
+ * buffer traffic at all: it isolates how fast this dtype's dot products run
10
+ * on the ALU once bandwidth is out of the picture. 32 MACs (64 FLOPs) per
11
+ * loop iteration.
12
+ */
13
+ export const flopsF16MatvecWgsl = /* wgsl */ `
14
+ enable f16;
15
+
16
+ struct Params {
17
+ threads: u32,
18
+ iterations: u32,
19
+ };
20
+
21
+ @group(0) @binding(0) var<uniform> params: Params;
22
+ @group(0) @binding(1) var<storage, read_write> out: array<f32>;
23
+
24
+ // Same weight hash as the fp32 kernel, but producing vec4<f16>.
25
+ fn weights(seed: u32) -> vec4<f16> {
26
+ let h = seed * 0x9e3779b1u;
27
+ let bytes = vec4<u32>(h & 0xffu, (h >> 8u) & 0xffu, (h >> 16u) & 0xffu, h >> 24u);
28
+ return (vec4<f16>(bytes) - vec4<f16>(127.5)) * f16(1.0 / 4096.0);
29
+ }
30
+
31
+ @compute @workgroup_size(64)
32
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
33
+ let idx = gid.x;
34
+ if (idx >= params.threads) {
35
+ return;
36
+ }
37
+ let w00: vec4<f16> = weights(idx * 8u + 1u);
38
+ let w01: vec4<f16> = weights(idx * 8u + 2u);
39
+ let w10: vec4<f16> = weights(idx * 8u + 3u);
40
+ let w11: vec4<f16> = weights(idx * 8u + 4u);
41
+ let w20: vec4<f16> = weights(idx * 8u + 5u);
42
+ let w21: vec4<f16> = weights(idx * 8u + 6u);
43
+ let w30: vec4<f16> = weights(idx * 8u + 7u);
44
+ let w31: vec4<f16> = weights(idx * 8u + 8u);
45
+ var x0: vec4<f16> = vec4<f16>(f16(idx & 255u) * f16(0.001) + f16(0.1), f16(0.2), f16(0.3), f16(0.4));
46
+ var x1: vec4<f16> = vec4<f16>(f16(idx & 255u) * f16(0.001) + f16(0.2), f16(0.4), f16(0.6), f16(0.8));
47
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
48
+ let o0 = dot(w00, x0) + dot(w01, x1);
49
+ let o1 = dot(w10, x0) + dot(w11, x1);
50
+ let o2 = dot(w20, x0) + dot(w21, x1);
51
+ let o3 = dot(w30, x0) + dot(w31, x1);
52
+ x0 = x1;
53
+ x1 = vec4<f16>(o0, o1, o2, o3);
54
+ }
55
+ out[idx] = f32(x0.x + x0.y + x0.z + x0.w + x1.x + x1.y + x1.z + x1.w);
56
+ }
57
+ `;
58
+ //# sourceMappingURL=flopsF16Matvec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsF16Matvec.js","sourceRoot":"","sources":["../../src/shaders/flopsF16Matvec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4C5C,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Raw fp16 FLOPS probe — same shape as {@link flopsF32ScalarWgsl} (eight
3
+ * independent chains, unrolled 4x), but every operand and accumulator is
4
+ * `f16`, so the FMA chains run entirely in half precision (only the final
5
+ * store converts back to f32). 64 FLOPs per loop iteration.
6
+ */
7
+ export declare const flopsF16ScalarWgsl = "\nenable f16;\n\nstruct Params {\n threads: u32,\n iterations: u32,\n};\n\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read_write> out: array<f32>;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let idx = gid.x;\n if (idx >= params.threads) {\n return;\n }\n let a: f16 = f16(1.0) - f16(idx & 15u) * f16(0.001);\n let b: f16 = f16(0.5) + f16(idx & 7u) * f16(0.001);\n var x0: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.01);\n var x1: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.02);\n var x2: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.03);\n var x3: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.04);\n var x4: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.05);\n var x5: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.06);\n var x6: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.07);\n var x7: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.08);\n for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {\n x0 = x0 * a + b;\n x1 = x1 * a + b;\n x2 = x2 * a + b;\n x3 = x3 * a + b;\n x4 = x4 * a + b;\n x5 = x5 * a + b;\n x6 = x6 * a + b;\n x7 = x7 * a + b;\n x0 = x0 * a + b;\n x1 = x1 * a + b;\n x2 = x2 * a + b;\n x3 = x3 * a + b;\n x4 = x4 * a + b;\n x5 = x5 * a + b;\n x6 = x6 * a + b;\n x7 = x7 * a + b;\n x0 = x0 * a + b;\n x1 = x1 * a + b;\n x2 = x2 * a + b;\n x3 = x3 * a + b;\n x4 = x4 * a + b;\n x5 = x5 * a + b;\n x6 = x6 * a + b;\n x7 = x7 * a + b;\n x0 = x0 * a + b;\n x1 = x1 * a + b;\n x2 = x2 * a + b;\n x3 = x3 * a + b;\n x4 = x4 * a + b;\n x5 = x5 * a + b;\n x6 = x6 * a + b;\n x7 = x7 * a + b;\n }\n out[idx] = f32(x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7);\n}\n";
8
+ //# sourceMappingURL=flopsF16Scalar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsF16Scalar.d.ts","sourceRoot":"","sources":["../../src/shaders/flopsF16Scalar.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,svDA+D9B,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Raw fp16 FLOPS probe — same shape as {@link flopsF32ScalarWgsl} (eight
3
+ * independent chains, unrolled 4x), but every operand and accumulator is
4
+ * `f16`, so the FMA chains run entirely in half precision (only the final
5
+ * store converts back to f32). 64 FLOPs per loop iteration.
6
+ */
7
+ export const flopsF16ScalarWgsl = /* wgsl */ `
8
+ enable f16;
9
+
10
+ struct Params {
11
+ threads: u32,
12
+ iterations: u32,
13
+ };
14
+
15
+ @group(0) @binding(0) var<uniform> params: Params;
16
+ @group(0) @binding(1) var<storage, read_write> out: array<f32>;
17
+
18
+ @compute @workgroup_size(64)
19
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
20
+ let idx = gid.x;
21
+ if (idx >= params.threads) {
22
+ return;
23
+ }
24
+ let a: f16 = f16(1.0) - f16(idx & 15u) * f16(0.001);
25
+ let b: f16 = f16(0.5) + f16(idx & 7u) * f16(0.001);
26
+ var x0: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.01);
27
+ var x1: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.02);
28
+ var x2: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.03);
29
+ var x3: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.04);
30
+ var x4: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.05);
31
+ var x5: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.06);
32
+ var x6: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.07);
33
+ var x7: f16 = f16(idx & 255u) * f16(0.0001) + f16(0.08);
34
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
35
+ x0 = x0 * a + b;
36
+ x1 = x1 * a + b;
37
+ x2 = x2 * a + b;
38
+ x3 = x3 * a + b;
39
+ x4 = x4 * a + b;
40
+ x5 = x5 * a + b;
41
+ x6 = x6 * a + b;
42
+ x7 = x7 * a + b;
43
+ x0 = x0 * a + b;
44
+ x1 = x1 * a + b;
45
+ x2 = x2 * a + b;
46
+ x3 = x3 * a + b;
47
+ x4 = x4 * a + b;
48
+ x5 = x5 * a + b;
49
+ x6 = x6 * a + b;
50
+ x7 = x7 * a + b;
51
+ x0 = x0 * a + b;
52
+ x1 = x1 * a + b;
53
+ x2 = x2 * a + b;
54
+ x3 = x3 * a + b;
55
+ x4 = x4 * a + b;
56
+ x5 = x5 * a + b;
57
+ x6 = x6 * a + b;
58
+ x7 = x7 * a + b;
59
+ x0 = x0 * a + b;
60
+ x1 = x1 * a + b;
61
+ x2 = x2 * a + b;
62
+ x3 = x3 * a + b;
63
+ x4 = x4 * a + b;
64
+ x5 = x5 * a + b;
65
+ x6 = x6 * a + b;
66
+ x7 = x7 * a + b;
67
+ }
68
+ out[idx] = f32(x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7);
69
+ }
70
+ `;
71
+ //# sourceMappingURL=flopsF16Scalar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsF16Scalar.js","sourceRoot":"","sources":["../../src/shaders/flopsF16Scalar.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+D5C,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * fp16 vec4 FLOPS probe — a single FMA chain held in a `vec4<f16>` register
3
+ * (4 independent half-precision lanes per step; only GPUs with packed-half
4
+ * ALUs run this faster than the fp32 vec4 probe).
5
+ */
6
+ export declare const flopsF16Vec4Wgsl = "\nenable f16;\n\nstruct Params {\n threads: u32,\n iterations: u32,\n};\n\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read_write> out: array<f32>;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let idx = gid.x;\n if (idx >= params.threads) {\n return;\n }\n let s: f16 = f16(idx & 15u) * f16(0.001);\n let half: f16 = f16(0.5);\n let one: f16 = f16(1.0);\n let a: vec4<f16> = vec4<f16>(one - s, one - f16(2.0) * s, one - f16(3.0) * s, one - f16(4.0) * s);\n let b: vec4<f16> = vec4<f16>(half + s, half + f16(2.0) * s, half + f16(3.0) * s, half + f16(4.0) * s);\n var x: vec4<f16> = vec4<f16>(f16(idx & 255u) * f16(0.0001));\n for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {\n x = x * a + b;\n }\n out[idx] = f32(x.x + x.y + x.z + x.w);\n}\n";
7
+ //# sourceMappingURL=flopsF16Vec4.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsF16Vec4.d.ts","sourceRoot":"","sources":["../../src/shaders/flopsF16Vec4.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,i2BA4B5B,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * fp16 vec4 FLOPS probe — a single FMA chain held in a `vec4<f16>` register
3
+ * (4 independent half-precision lanes per step; only GPUs with packed-half
4
+ * ALUs run this faster than the fp32 vec4 probe).
5
+ */
6
+ export const flopsF16Vec4Wgsl = /* wgsl */ `
7
+ enable f16;
8
+
9
+ struct Params {
10
+ threads: u32,
11
+ iterations: u32,
12
+ };
13
+
14
+ @group(0) @binding(0) var<uniform> params: Params;
15
+ @group(0) @binding(1) var<storage, read_write> out: array<f32>;
16
+
17
+ @compute @workgroup_size(64)
18
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
19
+ let idx = gid.x;
20
+ if (idx >= params.threads) {
21
+ return;
22
+ }
23
+ let s: f16 = f16(idx & 15u) * f16(0.001);
24
+ let half: f16 = f16(0.5);
25
+ let one: f16 = f16(1.0);
26
+ let a: vec4<f16> = vec4<f16>(one - s, one - f16(2.0) * s, one - f16(3.0) * s, one - f16(4.0) * s);
27
+ let b: vec4<f16> = vec4<f16>(half + s, half + f16(2.0) * s, half + f16(3.0) * s, half + f16(4.0) * s);
28
+ var x: vec4<f16> = vec4<f16>(f16(idx & 255u) * f16(0.0001));
29
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
30
+ x = x * a + b;
31
+ }
32
+ out[idx] = f32(x.x + x.y + x.z + x.w);
33
+ }
34
+ `;
35
+ //# sourceMappingURL=flopsF16Vec4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsF16Vec4.js","sourceRoot":"","sources":["../../src/shaders/flopsF16Vec4.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4B1C,CAAC"}