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 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAYxC,iGAAiG;AACjG,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAkB7D;AAED,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,8FAA8F;IAC9F,UAAU,EAAE,MAAM,CAAC;IACnB,uGAAuG;IACvG,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAQxF;AAED,wHAAwH;AACxH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAGxF"}
package/dist/stats.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Two-sided 97.5% quantiles of Student's t distribution (i.e. the multiplier
3
+ * for a 95% confidence interval) indexed by degrees of freedom 1..30. Beyond
4
+ * 30 the value is within ~4% of the normal 1.96, so the df=30 entry is reused.
5
+ */
6
+ const T_975 = [
7
+ 12.706, 4.303, 3.182, 2.776, 2.571, 2.447, 2.365, 2.306, 2.262, 2.228, 2.201, 2.179, 2.16, 2.145, 2.131, 2.12, 2.11,
8
+ 2.101, 2.093, 2.086, 2.08, 2.074, 2.069, 2.064, 2.06, 2.056, 2.052, 2.048, 2.045, 2.042,
9
+ ];
10
+ /** Student-t multiplier for a two-sided 95% confidence interval with `df` degrees of freedom. */
11
+ export function tQuantile975(df) {
12
+ if (df < 1)
13
+ return Number.POSITIVE_INFINITY;
14
+ return T_975[Math.min(df, T_975.length) - 1];
15
+ }
16
+ export function computeStats(values) {
17
+ if (values.length === 0) {
18
+ throw new Error('computeStats: values must be non-empty');
19
+ }
20
+ const n = values.length;
21
+ const sorted = values.toSorted((a, b) => a - b);
22
+ const sum = values.reduce((a, b) => a + b, 0);
23
+ const mean = sum / n;
24
+ const min = sorted[0];
25
+ const max = sorted[n - 1];
26
+ const mid = Math.floor(n / 2);
27
+ const median = n % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
28
+ const variance = n > 1 ? values.reduce((acc, v) => acc + (v - mean) ** 2, 0) / (n - 1) : 0;
29
+ const stddev = Math.sqrt(variance);
30
+ // Half-width of the 95% CI on the mean. With a single sample the
31
+ // t-multiplier is infinite (df = 0), so the interval is unbounded.
32
+ const ci95 = n > 1 ? (tQuantile975(n - 1) * stddev) / Math.sqrt(n) : Number.POSITIVE_INFINITY;
33
+ return { mean, min, max, median, stddev, ci95 };
34
+ }
35
+ /**
36
+ * Stopping rule for best-of-N sampling.
37
+ *
38
+ * The reported statistic is the *minimum* time, since every source of noise a
39
+ * benchmark meets (thermal throttling, clock ramp, compositor frames, other
40
+ * apps) only ever makes a run slower — the fastest run is the closest thing
41
+ * to the device's true capability. So sampling is done once the minimum has
42
+ * settled: the last `stableRuns` samples failed to beat the best seen before
43
+ * them by more than `tolerance`. Three tight samples converge immediately
44
+ * (minRuns 3, stableRuns 2: sample 1 sets the best, samples 2 and 3 confirm
45
+ * it); a still-ramping GPU that keeps producing faster runs keeps sampling.
46
+ *
47
+ * Degenerate data (non-positive or non-finite best) never converges, so the
48
+ * caller runs to its cap and can then diagnose the bad timings itself.
49
+ */
50
+ export function isBestStable(values, opts) {
51
+ const n = values.length;
52
+ if (n < Math.max(opts.stableRuns + 1, opts.minRuns, 2))
53
+ return false;
54
+ const best = Math.min(...values);
55
+ if (!Number.isFinite(best) || best <= 0)
56
+ return false;
57
+ const priorBest = Math.min(...values.slice(0, n - opts.stableRuns));
58
+ // Stable iff none of the trailing samples improved on the prior best by more than the tolerance.
59
+ return best >= priorBest * (1 - opts.tolerance);
60
+ }
61
+ /** True when `sampleMs` is more than `threshold` (fraction) slower than `bestMs` — the signature of a throttled run. */
62
+ export function isThrottled(sampleMs, bestMs, threshold) {
63
+ if (!Number.isFinite(bestMs) || bestMs <= 0)
64
+ return false;
65
+ return sampleMs > bestMs * (1 + threshold);
66
+ }
67
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,KAAK,GAAG;IACZ,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI;IACnH,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK;CACxF,CAAC;AAEF,iGAAiG;AACjG,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,IAAI,EAAE,GAAG,CAAC;QAAE,OAAO,MAAM,CAAC,iBAAiB,CAAC;IAC5C,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAyB;IACpD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC;IACrB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;IACvB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAE,GAAG,MAAM,CAAC,GAAG,CAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAE,CAAC;IAClF,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnC,iEAAiE;IACjE,mEAAmE;IACnE,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC;IAC9F,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAClD,CAAC;AAWD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,YAAY,CAAC,MAAyB,EAAE,IAAuB;IAC7E,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC;IACxB,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACrE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACpE,iGAAiG;IACjG,OAAO,IAAI,IAAI,SAAS,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;AAClD,CAAC;AAED,wHAAwH;AACxH,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,MAAc,EAAE,SAAiB;IAC7E,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1D,OAAO,QAAQ,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { type GpuContext } from './gpu/context.ts';
2
+ import type { BenchmarkResult, SuiteOptions } from './types.ts';
3
+ export type { BenchmarkResult, SuiteOptions, DeviceInfo, Stats, BenchmarkCategory, BenchmarkStatus, SamplingStopReason, SuiteProgressEvent, MetricDef, } from './types.ts';
4
+ export { computeStats } from './stats.ts';
5
+ /**
6
+ * Runs the full benchmark suite, yielding a BenchmarkResult every time a
7
+ * row changes so a UI can render the results table incrementally: once
8
+ * when each benchmark is set up (`running`), after every measurement (still
9
+ * `running`, with the best-so-far), and once when it finishes (ok / skipped
10
+ * / error).
11
+ *
12
+ * Every benchmark isolates a single resource — memory read, memory write,
13
+ * or ALU throughput — rather than modeling a specific real-world op, so
14
+ * results are directly comparable to the device's published bandwidth /
15
+ * FLOPS specs.
16
+ *
17
+ * Measurements are scheduled round-robin across all benchmarks with idle
18
+ * gaps and thermal cooldowns (see `runSampling`), and the reported number
19
+ * for each benchmark is its *best* run — so a phone that throttles partway
20
+ * through the suite still reports what it can do rather than what it was
21
+ * doing while hot.
22
+ */
23
+ export declare function runSuite(options?: SuiteOptions): AsyncGenerator<BenchmarkResult>;
24
+ export type { GpuContext };
25
+ //# sourceMappingURL=suite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suite.d.ts","sourceRoot":"","sources":["../src/suite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAkDtE,OAAO,KAAK,EAAqB,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEnF,YAAY,EACV,eAAe,EACf,YAAY,EACZ,UAAU,EACV,KAAK,EACL,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,SAAS,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAgB1C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAuB,QAAQ,CAAC,OAAO,GAAE,YAAiB,GAAG,cAAc,CAAC,eAAe,CAAC,CAiI3F;AAyCD,YAAY,EAAE,UAAU,EAAE,CAAC"}
package/dist/suite.js ADDED
@@ -0,0 +1,196 @@
1
+ import { acquireGpuContext } from "./gpu/context.js";
2
+ import { generateMatVecData, padToMultipleOf4 } from "./data/generate.js";
3
+ import { KernelSampler } from "./gpu/benchmarkRunner.js";
4
+ import { runSampling } from "./sampling.js";
5
+ import { prepareReadBandwidth, prepareWriteBandwidth } from "./benchmarks/streamBandwidth.js";
6
+ import { prepareFlopsF32Scalar, prepareFlopsF32Vec4, prepareFlopsF32Mat4, prepareFlopsF32Matvec, } from "./benchmarks/flopsF32.js";
7
+ import { prepareFlopsF16Scalar, prepareFlopsF16Vec4, prepareFlopsF16Mat4, prepareFlopsF16Matvec, } from "./benchmarks/flopsF16.js";
8
+ import { prepareFlopsI8Scalar, prepareFlopsI8Vec4, prepareFlopsI8Mat4, prepareFlopsI8Matvec, prepareFlopsI8MatvecDp4a, prepareFlopsI8Dp4a, } from "./benchmarks/flopsI8.js";
9
+ import { prepareFlopsF32Div, prepareFlopsI32Div, prepareFlopsF32Sqrt, prepareFlopsF32Rsqrt, prepareFlopsF32Pow, prepareFlopsF32Sincos, prepareFlopsF32Log, } from "./benchmarks/flopsMath.js";
10
+ import { prepareFlopsU32PackUnpack, prepareFlopsI32F32Convert, prepareFlopsF32F16Convert, prepareFlopsI32F16Convert, } from "./benchmarks/flopsConvert.js";
11
+ import { errorResult, metricPerSecond, rowFromMeta, FLOPS_METRIC, BYTES_METRIC, } from "./benchmarks/common.js";
12
+ export { computeStats } from "./stats.js";
13
+ const DEFAULT_ROWS = 4096;
14
+ const DEFAULT_COLS = 4096;
15
+ const bandwidth = (id, prepare) => [id, 'bandwidth', prepare];
16
+ const compute = (id, prepare) => [id, 'compute', prepare];
17
+ /**
18
+ * Runs the full benchmark suite, yielding a BenchmarkResult every time a
19
+ * row changes so a UI can render the results table incrementally: once
20
+ * when each benchmark is set up (`running`), after every measurement (still
21
+ * `running`, with the best-so-far), and once when it finishes (ok / skipped
22
+ * / error).
23
+ *
24
+ * Every benchmark isolates a single resource — memory read, memory write,
25
+ * or ALU throughput — rather than modeling a specific real-world op, so
26
+ * results are directly comparable to the device's published bandwidth /
27
+ * FLOPS specs.
28
+ *
29
+ * Measurements are scheduled round-robin across all benchmarks with idle
30
+ * gaps and thermal cooldowns (see `runSampling`), and the reported number
31
+ * for each benchmark is its *best* run — so a phone that throttles partway
32
+ * through the suite still reports what it can do rather than what it was
33
+ * doing while hot.
34
+ */
35
+ export async function* runSuite(options = {}) {
36
+ const rows = padToMultipleOf4(options.rows ?? DEFAULT_ROWS);
37
+ const cols = padToMultipleOf4(options.cols ?? DEFAULT_COLS);
38
+ const ctx = await acquireGpuContext();
39
+ options.onDeviceInfo?.(ctx.info);
40
+ const data = generateMatVecData(rows, cols);
41
+ const harness = {
42
+ targetMs: options.targetMs,
43
+ targetDispatchMs: options.targetDispatchMs,
44
+ warmups: options.warmups,
45
+ };
46
+ const flopsHarness = { ...harness, threads: options.computeThreads, iterations: options.computeIterations };
47
+ const benchmarks = [
48
+ bandwidth('read-bandwidth', () => prepareReadBandwidth(ctx, data, harness)),
49
+ bandwidth('write-bandwidth', () => prepareWriteBandwidth(ctx, data, harness)),
50
+ compute('flops-f32-scalar', () => prepareFlopsF32Scalar(ctx, flopsHarness)),
51
+ compute('flops-f32-vec4', () => prepareFlopsF32Vec4(ctx, flopsHarness)),
52
+ compute('flops-f32-mat4', () => prepareFlopsF32Mat4(ctx, flopsHarness)),
53
+ compute('flops-f32-matvec', () => prepareFlopsF32Matvec(ctx, flopsHarness)),
54
+ compute('flops-f16-scalar', () => prepareFlopsF16Scalar(ctx, flopsHarness)),
55
+ compute('flops-f16-vec4', () => prepareFlopsF16Vec4(ctx, flopsHarness)),
56
+ compute('flops-f16-mat4', () => prepareFlopsF16Mat4(ctx, flopsHarness)),
57
+ compute('flops-f16-matvec', () => prepareFlopsF16Matvec(ctx, flopsHarness)),
58
+ compute('flops-i8-scalar', () => prepareFlopsI8Scalar(ctx, flopsHarness)),
59
+ compute('flops-i8-vec4', () => prepareFlopsI8Vec4(ctx, flopsHarness)),
60
+ compute('flops-i8-mat4', () => prepareFlopsI8Mat4(ctx, flopsHarness)),
61
+ compute('flops-i8-matvec', () => prepareFlopsI8Matvec(ctx, flopsHarness)),
62
+ compute('flops-i8-matvec-dp4a', () => prepareFlopsI8MatvecDp4a(ctx, flopsHarness)),
63
+ compute('flops-i8-dp4a', () => prepareFlopsI8Dp4a(ctx, flopsHarness)),
64
+ compute('flops-f32-div', () => prepareFlopsF32Div(ctx, flopsHarness)),
65
+ compute('flops-i32-div', () => prepareFlopsI32Div(ctx, flopsHarness)),
66
+ compute('flops-f32-sqrt', () => prepareFlopsF32Sqrt(ctx, flopsHarness)),
67
+ compute('flops-f32-rsqrt', () => prepareFlopsF32Rsqrt(ctx, flopsHarness)),
68
+ compute('flops-f32-pow', () => prepareFlopsF32Pow(ctx, flopsHarness)),
69
+ compute('flops-f32-sincos', () => prepareFlopsF32Sincos(ctx, flopsHarness)),
70
+ compute('flops-f32-log', () => prepareFlopsF32Log(ctx, flopsHarness)),
71
+ compute('flops-u32-packunpack', () => prepareFlopsU32PackUnpack(ctx, flopsHarness)),
72
+ compute('flops-i32-f32-convert', () => prepareFlopsI32F32Convert(ctx, flopsHarness)),
73
+ compute('flops-f32-f16-convert', () => prepareFlopsF32F16Convert(ctx, flopsHarness)),
74
+ compute('flops-i32-f16-convert', () => prepareFlopsI32F16Convert(ctx, flopsHarness)),
75
+ ];
76
+ // Phase 1: build every benchmark's GPU resources up front. Rows that can't
77
+ // run at all (missing feature, failed shader compile) resolve right here;
78
+ // the rest show up as `running` so the table has its final shape before
79
+ // the first measurement lands.
80
+ const scheduled = [];
81
+ for (const [id, category, prepare] of benchmarks) {
82
+ const fallbackMeta = {
83
+ id,
84
+ label: id,
85
+ description: '',
86
+ source: '',
87
+ category,
88
+ rows,
89
+ cols,
90
+ metric: category === 'bandwidth' ? BYTES_METRIC : FLOPS_METRIC,
91
+ amountPerOp: 0,
92
+ };
93
+ let prepared;
94
+ try {
95
+ prepared = await prepare();
96
+ }
97
+ catch (error) {
98
+ yield errorResult(fallbackMeta, error);
99
+ continue;
100
+ }
101
+ if (prepared.kind === 'skipped') {
102
+ yield prepared.result;
103
+ continue;
104
+ }
105
+ scheduled.push({
106
+ meta: prepared.meta,
107
+ sampler: new KernelSampler(prepared.harness),
108
+ metaAtWork: prepared.metaAtWork,
109
+ });
110
+ yield rowFromMeta(prepared.meta);
111
+ }
112
+ // Phase 2: sample everything round-robin. `runSampling` pushes updates
113
+ // through a callback; bridge them into this generator via a queue.
114
+ const byId = new Map(scheduled.map((k) => [k.meta.id, k]));
115
+ const queue = [];
116
+ let wake = null;
117
+ const push = (r) => {
118
+ queue.push(r);
119
+ wake?.();
120
+ wake = null;
121
+ };
122
+ const sampleables = scheduled.map(({ meta, sampler }) => ({
123
+ id: meta.id,
124
+ calibrate: () => sampler.calibrate(),
125
+ sample: () => sampler.sample(),
126
+ }));
127
+ const run = runSampling(sampleables, {
128
+ ...pickSamplingOptions(options),
129
+ onProgress: options.onProgress,
130
+ onUpdate: (state) => push(resultFromState(byId.get(state.id), state)),
131
+ }).finally(() => {
132
+ for (const k of scheduled)
133
+ k.sampler.destroy();
134
+ });
135
+ let done = false;
136
+ void run.then(() => {
137
+ done = true;
138
+ wake?.();
139
+ }, () => {
140
+ done = true;
141
+ wake?.();
142
+ });
143
+ try {
144
+ while (true) {
145
+ while (queue.length > 0)
146
+ yield queue.shift();
147
+ if (done)
148
+ break;
149
+ await new Promise((resolve) => {
150
+ wake = resolve;
151
+ });
152
+ }
153
+ await run;
154
+ }
155
+ finally {
156
+ ctx.device.destroy();
157
+ }
158
+ }
159
+ function pickSamplingOptions(o) {
160
+ return {
161
+ minRounds: o.minRounds,
162
+ maxRounds: o.maxRounds,
163
+ stableRounds: o.stableRounds,
164
+ improvementTolerance: o.improvementTolerance,
165
+ throttleThreshold: o.throttleThreshold,
166
+ idleMs: o.idleMs,
167
+ cooldownMs: o.cooldownMs,
168
+ maxCooldowns: o.maxCooldowns,
169
+ throttledFraction: o.throttledFraction,
170
+ };
171
+ }
172
+ function resultFromState(kernel, state) {
173
+ const { sampler } = kernel;
174
+ // Once calibrated, the problem size (and so FLOPs per op) is whatever the sampler settled on.
175
+ const meta = kernel.metaAtWork && sampler.work !== undefined ? kernel.metaAtWork(sampler.work) : kernel.meta;
176
+ if (state.error !== undefined)
177
+ return errorResult(meta, state.error);
178
+ const row = rowFromMeta(meta);
179
+ row.status = state.stopReason ? 'ok' : 'running';
180
+ row.innerIterations = sampler.innerIterations;
181
+ row.timingMethod = sampler.timingMethod;
182
+ row.timesMs = state.timesMs;
183
+ row.throttledMs = state.throttledMs;
184
+ row.stats = state.stats;
185
+ row.stopReason = state.stopReason;
186
+ if (state.stats) {
187
+ // Headline number comes from the best run: every noise source only ever
188
+ // slows a run down, so the minimum is the least-contaminated estimate.
189
+ row.metricValue = metricPerSecond(meta.amountPerOp, state.stats.min);
190
+ }
191
+ if (state.stopReason === 'throttled') {
192
+ row.message = 'Device stayed thermally throttled through every cooldown; best run may understate it.';
193
+ }
194
+ return row;
195
+ }
196
+ //# sourceMappingURL=suite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suite.js","sourceRoot":"","sources":["../src/suite.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,171 @@
1
+ /** Summary statistics computed over the kept (non-throttled) timed measurements. */
2
+ export interface Stats {
3
+ mean: number;
4
+ /** The best (fastest) kept measurement — the headline number, see `BenchmarkResult.metricValue`. */
5
+ min: number;
6
+ max: number;
7
+ median: number;
8
+ stddev: number;
9
+ /** Half-width of the 95% confidence interval on the mean (Student-t); Infinity with a single sample. */
10
+ ci95: number;
11
+ }
12
+ /**
13
+ * Why sampling stopped for a benchmark:
14
+ * - `converged`: the best run stopped improving (see `SuiteOptions.stableRounds`).
15
+ * - `max-rounds`: the per-benchmark cap was hit — the best was still improving, or the benchmark
16
+ * kept producing throttled runs on its own while the rest of the suite was fine.
17
+ * - `throttled`: the device kept coming back throttled even after the suite's cooldown pauses
18
+ * were exhausted, so the best run may understate the device (treat the row with suspicion).
19
+ */
20
+ export type SamplingStopReason = 'converged' | 'max-rounds' | 'throttled';
21
+ export type TimingMethod = 'gpu-timestamp' | 'cpu-wallclock';
22
+ /** `bandwidth`: streams memory with ~no compute. `compute`: pure ALU with ~no memory traffic. */
23
+ export type BenchmarkCategory = 'bandwidth' | 'compute';
24
+ export type BenchmarkStatus = 'running' | 'ok' | 'skipped' | 'error';
25
+ /**
26
+ * Identifies what a benchmark's throughput number counts. `key` is a
27
+ * JSON-safe id shared by every benchmark that measures the same kind of
28
+ * thing (so it's stable to key off, not unique per-benchmark); `unit` is
29
+ * the short symbol shown as `<unit>/s` (FLOP, OP, B); `name` is a short
30
+ * friendly label for display (e.g. in a tooltip).
31
+ */
32
+ export interface MetricDef {
33
+ key: string;
34
+ unit: string;
35
+ name: string;
36
+ }
37
+ /**
38
+ * One row of the results table. `runSuite` yields a fresh object for a given
39
+ * `id` every time the row changes — when the benchmark starts (`running`),
40
+ * after every sample it takes (still `running`, with the partial stats so
41
+ * far), and when it finishes (ok/skipped/error) — so the UI can render
42
+ * progress incrementally.
43
+ */
44
+ export interface BenchmarkResult {
45
+ id: string;
46
+ label: string;
47
+ description: string;
48
+ /** WGSL source of the kernel that was run, for display alongside the result. */
49
+ source: string;
50
+ category: BenchmarkCategory;
51
+ status: BenchmarkStatus;
52
+ message?: string;
53
+ /**
54
+ * Logical problem size actually benchmarked. For `bandwidth` kernels:
55
+ * the streamed buffer's rows/cols (after padding to a multiple of 4).
56
+ * For `compute` kernels: thread count / in-shader loop trip count (as
57
+ * calibrated for this GPU, see `SuiteOptions.targetDispatchMs`).
58
+ */
59
+ rows: number;
60
+ cols: number;
61
+ /** Number of GPU dispatches batched into each timed measurement. */
62
+ innerIterations: number;
63
+ /** Per-op time (ms) for each kept timed measurement, in the order taken. Length varies: sampling is adaptive. */
64
+ timesMs: number[];
65
+ /**
66
+ * Per-op time (ms) for measurements that were discarded as throttled —
67
+ * more than `throttleThreshold` slower than the best run seen so far. Kept for diagnostics; never feeds `stats`.
68
+ */
69
+ throttledMs: number[];
70
+ /** Summary over `timesMs`. `stats.min` is the best run. */
71
+ stats?: Stats;
72
+ /** Set for `ok` rows: see `SamplingStopReason`. */
73
+ stopReason?: SamplingStopReason;
74
+ /** What `metricValue` counts. Set from the start, before any measurement lands. */
75
+ metric: MetricDef;
76
+ /** Peak `<metric.unit>/s`, computed from the best run (`stats.min`). */
77
+ metricValue?: number;
78
+ timingMethod: TimingMethod;
79
+ }
80
+ /** Suite-level progress events (round boundaries and thermal cooldown pauses). */
81
+ export type SuiteProgressEvent = {
82
+ type: 'round';
83
+ round: number;
84
+ active: number;
85
+ } | {
86
+ type: 'cooldown';
87
+ attempt: number;
88
+ maxAttempts: number;
89
+ ms: number;
90
+ throttledIds: string[];
91
+ } | {
92
+ type: 'throttle-abort';
93
+ throttledIds: string[];
94
+ };
95
+ export interface SuiteOptions {
96
+ /** Streamed buffer rows (for the bandwidth benchmarks). Padded up to a multiple of 4. Default 4096. */
97
+ rows?: number;
98
+ /** Streamed buffer cols (for the bandwidth benchmarks). Padded up to a multiple of 4. Default 4096. */
99
+ cols?: number;
100
+ /**
101
+ * Target duration of a single timed measurement, in ms. Sets how many
102
+ * dispatches get batched per measurement. Kept short (default 100) so the
103
+ * suite's total GPU-busy time — and therefore heat — stays low.
104
+ */
105
+ targetMs?: number;
106
+ /**
107
+ * Target duration of a single GPU dispatch, in ms. A dispatch can't be
108
+ * preempted, so a long one freezes the display for its whole duration;
109
+ * the raw-FLOPS kernels have their loop trip count calibrated (from a tiny
110
+ * first probe upward, never more than 4x per step) so one dispatch lands
111
+ * near this on whatever GPU is running. Default 10.
112
+ */
113
+ targetDispatchMs?: number;
114
+ /** Number of discarded warmup measurements per benchmark before timing starts (the calibration pass is a further untimed run). Default 1. */
115
+ warmups?: number;
116
+ /** Fewest kept measurements per benchmark before it can be declared converged. Default 3. */
117
+ minRounds?: number;
118
+ /** Most kept measurements per benchmark, reached only if the best run keeps improving. Default 10. */
119
+ maxRounds?: number;
120
+ /**
121
+ * A benchmark is converged once its best run has not improved by more than
122
+ * `improvementTolerance` over this many consecutive kept measurements.
123
+ * Default 2.
124
+ */
125
+ stableRounds?: number;
126
+ /** Relative improvement of the best run that counts as "still improving" for `stableRounds`. Default 0.01 (1%). */
127
+ improvementTolerance?: number;
128
+ /**
129
+ * A measurement more than this fraction slower than the benchmark's best
130
+ * run is classified as thermally throttled and discarded. Default 0.20
131
+ * (20%): real throttling is 20–50%, while ordinary wall-clock jitter on
132
+ * Safari runs to ~15%.
133
+ */
134
+ throttleThreshold?: number;
135
+ /** Idle gap between consecutive measurements, in ms, so the GPU duty-cycles instead of running flat out. Default 100. */
136
+ idleMs?: number;
137
+ /** How long the whole suite pauses when it detects throttling, in ms. Default 3000. */
138
+ cooldownMs?: number;
139
+ /** How many cooldown pauses to attempt before giving up and reporting the still-unconverged benchmarks as `throttled`. Default 3. */
140
+ maxCooldowns?: number;
141
+ /**
142
+ * Suite-wide throttle trigger: a cooldown starts when at least this
143
+ * fraction (and at least two) of the benchmarks measured in a round come
144
+ * back throttled. Default 0.5.
145
+ */
146
+ throttledFraction?: number;
147
+ /** Number of GPU threads (invocations) launched by the raw-FLOPS compute benchmarks. Default 1,048,576. */
148
+ computeThreads?: number;
149
+ /**
150
+ * Pins the trip count of the in-shader loop in the raw-FLOPS compute
151
+ * benchmarks, disabling per-dispatch calibration. Left unset, each kernel's
152
+ * default (256–1024) is the *ceiling* and the sampler picks the count that
153
+ * makes one dispatch take ~`targetDispatchMs`.
154
+ */
155
+ computeIterations?: number;
156
+ /** Called once with the resolved GPU device/adapter info before benchmarks start. */
157
+ onDeviceInfo?: (info: DeviceInfo) => void;
158
+ /** Called at round boundaries and when the suite pauses for a thermal cooldown. */
159
+ onProgress?: (event: SuiteProgressEvent) => void;
160
+ }
161
+ export interface DeviceInfo {
162
+ vendor?: string;
163
+ architecture?: string;
164
+ description?: string;
165
+ features: string[];
166
+ limits: Record<string, number>;
167
+ supportsF16: boolean;
168
+ supportsI8Dot: boolean;
169
+ supportsTimestampQuery: boolean;
170
+ }
171
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "webgpu-bench",
3
+ "version": "0.1.0",
4
+ "description": "Benchmark suite for WebGPU's raw ceilings: memory bandwidth and fp32/fp16/int8 FLOPS.",
5
+ "keywords": [
6
+ "webgpu",
7
+ "benchmark",
8
+ "gpu",
9
+ "flops",
10
+ "bandwidth",
11
+ "profiler",
12
+ "wgsl",
13
+ "vgpu"
14
+ ],
15
+ "homepage": "https://github.com/bhouston/webgpu-profiler",
16
+ "bugs": {
17
+ "url": "https://github.com/bhouston/webgpu-profiler/issues"
18
+ },
19
+ "license": "MIT",
20
+ "author": "Ben Houston (https://ben3d.ca)",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/bhouston/webgpu-profiler.git",
24
+ "directory": "packages/webgpu-bench"
25
+ },
26
+ "type": "module",
27
+ "sideEffects": false,
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "dev": "tsc -w --preserveWatchOutput --noEmitOnError",
33
+ "test": "vitest run",
34
+ "test:browser": "vitest run --project browser"
35
+ },
36
+ "dependencies": {
37
+ "vgpu": "^0.4.0"
38
+ },
39
+ "devDependencies": {
40
+ "@webgpu/types": "^0.1.69"
41
+ },
42
+ "engines": {
43
+ "node": ">=22.0.0"
44
+ }
45
+ }