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,49 @@
1
+ /**
2
+ * int8 packed-dot-product FLOPS probe: uses the `packed_4x8_integer_dot_product`
3
+ * extension's `dot4I8Packed` builtin directly (two u32s, each four packed
4
+ * signed int8 lanes, one instruction) in a tight accumulation loop, to
5
+ * isolate the instruction's peak throughput. `a` is fed back from the
6
+ * running accumulator every iteration (not just `idx`-derived and fixed),
7
+ * so `dot4I8Packed(a, b)` is genuinely loop-carried — a compiler that
8
+ * noticed `a`/`b` were loop-invariant could otherwise hoist the call out of
9
+ * the loop and replace all `iterations` of it with one call + a multiply,
10
+ * which is exactly what happened before this fix (the loop collapsed to
11
+ * ~free, and the dispatch finished faster than the timestamp-query clock's
12
+ * resolution, reading back as a literal 0ns elapsed).
13
+ *
14
+ * `packed_4x8_integer_dot_product` is a WGSL *language extension* (gated by
15
+ * `navigator.gpu.wgslLanguageFeatures`, checked as `ctx.info.supportsI8Dot`
16
+ * in flopsCommon.ts), not an enable-extension tied to a GPUFeatureName like
17
+ * `f16` — so it's opted into with the `requires` directive, not `enable`.
18
+ * Using `enable` here fails shader compilation ("Expected 'clip_distances',
19
+ * 'f16', or 'primitive_index'" — the compiler listing valid enable-extension
20
+ * names), which used to go undetected and read back as a silent 0ns result.
21
+ */
22
+ export const flopsI8Dp4aWgsl = /* wgsl */ `
23
+ requires packed_4x8_integer_dot_product;
24
+
25
+ struct Params {
26
+ threads: u32,
27
+ iterations: u32,
28
+ };
29
+
30
+ @group(0) @binding(0) var<uniform> params: Params;
31
+ @group(0) @binding(1) var<storage, read_write> out: array<f32>;
32
+
33
+ @compute @workgroup_size(64)
34
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
35
+ let idx = gid.x;
36
+ if (idx >= params.threads) {
37
+ return;
38
+ }
39
+ let b: u32 = 0x05060708u ^ (idx * 0x85ebca6bu);
40
+ var a: u32 = 0x01020304u ^ (idx * 0x9e3779b1u);
41
+ var acc: i32 = i32(idx & 255u);
42
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
43
+ acc = acc + dot4I8Packed(a, b);
44
+ a = a ^ u32(acc);
45
+ }
46
+ out[idx] = f32(acc);
47
+ }
48
+ `;
49
+ //# sourceMappingURL=flopsI8Dp4a.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8Dp4a.js","sourceRoot":"","sources":["../../src/shaders/flopsI8Dp4a.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BzC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * int8-range mat4 FLOPS probe. WGSL has no `mat4x4<i32>` type (matrices
3
+ * are f32/f16-only), so the 4x4 integer matrix is emulated as four
4
+ * `vec4<i32>` rows and the matvec is built from `dot()` (which WGSL defines
5
+ * for integer vectors too) — the same 4 dot-products + write pattern a
6
+ * real int4x4 * int4 multiply would compile down to.
7
+ */
8
+ export declare const flopsI8Mat4Wgsl = "\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 // Every matrix entry is runtime-derived: a literal 1 here would let the\n // compiler fold 1 * x away and skip three quarters of the multiplies.\n let s: i32 = i32(idx & 15u) + 1;\n let t: i32 = i32((idx >> 4u) & 15u) + 2;\n let u: i32 = i32((idx >> 8u) & 15u) + 3;\n let v: i32 = i32((idx >> 12u) & 15u) + 4;\n let m0: vec4<i32> = vec4<i32>(s, t, u, v);\n let m1: vec4<i32> = vec4<i32>(v, s, t, u);\n let m2: vec4<i32> = vec4<i32>(u, v, s, t);\n let m3: vec4<i32> = vec4<i32>(t, u, v, s);\n let c: vec4<i32> = vec4<i32>(s, t, u, v);\n var x: vec4<i32> = vec4<i32>(i32(idx & 255u));\n for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {\n x = vec4<i32>(dot(m0, x), dot(m1, x), dot(m2, x), dot(m3, x)) + c;\n }\n out[idx] = f32(x.x + x.y + x.z + x.w);\n}\n";
9
+ //# sourceMappingURL=flopsI8Mat4.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8Mat4.d.ts","sourceRoot":"","sources":["../../src/shaders/flopsI8Mat4.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,smCAgC3B,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * int8-range mat4 FLOPS probe. WGSL has no `mat4x4<i32>` type (matrices
3
+ * are f32/f16-only), so the 4x4 integer matrix is emulated as four
4
+ * `vec4<i32>` rows and the matvec is built from `dot()` (which WGSL defines
5
+ * for integer vectors too) — the same 4 dot-products + write pattern a
6
+ * real int4x4 * int4 multiply would compile down to.
7
+ */
8
+ export const flopsI8Mat4Wgsl = /* wgsl */ `
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
+ // Every matrix entry is runtime-derived: a literal 1 here would let the
24
+ // compiler fold 1 * x away and skip three quarters of the multiplies.
25
+ let s: i32 = i32(idx & 15u) + 1;
26
+ let t: i32 = i32((idx >> 4u) & 15u) + 2;
27
+ let u: i32 = i32((idx >> 8u) & 15u) + 3;
28
+ let v: i32 = i32((idx >> 12u) & 15u) + 4;
29
+ let m0: vec4<i32> = vec4<i32>(s, t, u, v);
30
+ let m1: vec4<i32> = vec4<i32>(v, s, t, u);
31
+ let m2: vec4<i32> = vec4<i32>(u, v, s, t);
32
+ let m3: vec4<i32> = vec4<i32>(t, u, v, s);
33
+ let c: vec4<i32> = vec4<i32>(s, t, u, v);
34
+ var x: vec4<i32> = vec4<i32>(i32(idx & 255u));
35
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
36
+ x = vec4<i32>(dot(m0, x), dot(m1, x), dot(m2, x), dot(m3, x)) + c;
37
+ }
38
+ out[idx] = f32(x.x + x.y + x.z + x.w);
39
+ }
40
+ `;
41
+ //# sourceMappingURL=flopsI8Mat4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8Mat4.js","sourceRoot":"","sources":["../../src/shaders/flopsI8Mat4.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgCzC,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Register-resident matvec tile, int8-range weights unpacked to i32: a 4-row x 8-column weight tile held in
3
+ * registers (8 x vec4<i32>, 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 integer 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
+ * The outputs are shifted back down into int8 range before being fed back
13
+ * (integer wraparound keeps it bounded regardless).
14
+ */
15
+ export declare const flopsI8MatvecWgsl = "\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// Hashes a seed into four int8-range weights, held unpacked as a vec4<i32>\n// (what a dequantize-on-load kernel holds after unpack4xI8).\nfn weights(seed: u32) -> vec4<i32> {\n return unpack4xI8(seed * 0x9e3779b1u);\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<i32> = weights(idx * 8u + 1u);\n let w01: vec4<i32> = weights(idx * 8u + 2u);\n let w10: vec4<i32> = weights(idx * 8u + 3u);\n let w11: vec4<i32> = weights(idx * 8u + 4u);\n let w20: vec4<i32> = weights(idx * 8u + 5u);\n let w21: vec4<i32> = weights(idx * 8u + 6u);\n let w30: vec4<i32> = weights(idx * 8u + 7u);\n let w31: vec4<i32> = weights(idx * 8u + 8u);\n var x0: vec4<i32> = vec4<i32>(i32(idx & 255u) + 1, 2, 3, 4);\n var x1: vec4<i32> = vec4<i32>(i32(idx & 255u) + 2, 4, 6, 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<i32>(o0, o1, o2, o3) >> vec4<u32>(4u);\n }\n out[idx] = f32(x0.x + x0.y + x0.z + x0.w + x1.x + x1.y + x1.z + x1.w);\n}\n";
16
+ //# sourceMappingURL=flopsI8Matvec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8Matvec.d.ts","sourceRoot":"","sources":["../../src/shaders/flopsI8Matvec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,y7CAyC7B,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Register-resident matvec tile, int8-range weights unpacked to i32: a 4-row x 8-column weight tile held in
3
+ * registers (8 x vec4<i32>, 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 integer 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
+ * The outputs are shifted back down into int8 range before being fed back
13
+ * (integer wraparound keeps it bounded regardless).
14
+ */
15
+ export const flopsI8MatvecWgsl = /* wgsl */ `
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
+ // Hashes a seed into four int8-range weights, held unpacked as a vec4<i32>
25
+ // (what a dequantize-on-load kernel holds after unpack4xI8).
26
+ fn weights(seed: u32) -> vec4<i32> {
27
+ return unpack4xI8(seed * 0x9e3779b1u);
28
+ }
29
+
30
+ @compute @workgroup_size(64)
31
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
32
+ let idx = gid.x;
33
+ if (idx >= params.threads) {
34
+ return;
35
+ }
36
+ let w00: vec4<i32> = weights(idx * 8u + 1u);
37
+ let w01: vec4<i32> = weights(idx * 8u + 2u);
38
+ let w10: vec4<i32> = weights(idx * 8u + 3u);
39
+ let w11: vec4<i32> = weights(idx * 8u + 4u);
40
+ let w20: vec4<i32> = weights(idx * 8u + 5u);
41
+ let w21: vec4<i32> = weights(idx * 8u + 6u);
42
+ let w30: vec4<i32> = weights(idx * 8u + 7u);
43
+ let w31: vec4<i32> = weights(idx * 8u + 8u);
44
+ var x0: vec4<i32> = vec4<i32>(i32(idx & 255u) + 1, 2, 3, 4);
45
+ var x1: vec4<i32> = vec4<i32>(i32(idx & 255u) + 2, 4, 6, 8);
46
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
47
+ let o0 = dot(w00, x0) + dot(w01, x1);
48
+ let o1 = dot(w10, x0) + dot(w11, x1);
49
+ let o2 = dot(w20, x0) + dot(w21, x1);
50
+ let o3 = dot(w30, x0) + dot(w31, x1);
51
+ x0 = x1;
52
+ x1 = vec4<i32>(o0, o1, o2, o3) >> vec4<u32>(4u);
53
+ }
54
+ out[idx] = f32(x0.x + x0.y + x0.z + x0.w + x1.x + x1.y + x1.z + x1.w);
55
+ }
56
+ `;
57
+ //# sourceMappingURL=flopsI8Matvec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8Matvec.js","sourceRoot":"","sources":["../../src/shaders/flopsI8Matvec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyC3C,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Register-resident matvec tile, packed int8: a 4-row x 8-column weight tile held in
3
+ * registers (8 x u32 (4 packed int8 lanes each), 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 dot4I8Packed() 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
+ * Uses the packed_4x8_integer_dot_product WGSL language extension (opted in
13
+ * with `requires`, see flopsI8Dp4a.ts); the 4 outputs are
14
+ * shifted down and re-packed with pack4xI8 to form the next input word.
15
+ */
16
+ export declare const flopsI8MatvecDp4aWgsl = "\nrequires packed_4x8_integer_dot_product;\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// Hashes a seed into a u32 holding four packed int8 weights, exactly the\n// storage format dot4I8Packed consumes.\nfn weights(seed: u32) -> u32 {\n return seed * 0x9e3779b1u;\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: u32 = weights(idx * 8u + 1u);\n let w01: u32 = weights(idx * 8u + 2u);\n let w10: u32 = weights(idx * 8u + 3u);\n let w11: u32 = weights(idx * 8u + 4u);\n let w20: u32 = weights(idx * 8u + 5u);\n let w21: u32 = weights(idx * 8u + 6u);\n let w30: u32 = weights(idx * 8u + 7u);\n let w31: u32 = weights(idx * 8u + 8u);\n var x0: u32 = (idx * 0x85ebca6bu) ^ 0x01020304u;\n var x1: u32 = (idx * 0x85ebca6bu) ^ 0x02040608u;\n for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {\n let o0 = dot4I8Packed(w00, x0) + dot4I8Packed(w01, x1);\n let o1 = dot4I8Packed(w10, x0) + dot4I8Packed(w11, x1);\n let o2 = dot4I8Packed(w20, x0) + dot4I8Packed(w21, x1);\n let o3 = dot4I8Packed(w30, x0) + dot4I8Packed(w31, x1);\n x0 = x1;\n x1 = pack4xI8(vec4<i32>(o0, o1, o2, o3) >> vec4<u32>(4u));\n }\n out[idx] = f32(i32(x0) + i32(x1));\n}\n";
17
+ //# sourceMappingURL=flopsI8MatvecDp4a.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8MatvecDp4a.d.ts","sourceRoot":"","sources":["../../src/shaders/flopsI8MatvecDp4a.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,k6CA2CjC,CAAC"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Register-resident matvec tile, packed int8: a 4-row x 8-column weight tile held in
3
+ * registers (8 x u32 (4 packed int8 lanes each), 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 dot4I8Packed() 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
+ * Uses the packed_4x8_integer_dot_product WGSL language extension (opted in
13
+ * with `requires`, see flopsI8Dp4a.ts); the 4 outputs are
14
+ * shifted down and re-packed with pack4xI8 to form the next input word.
15
+ */
16
+ export const flopsI8MatvecDp4aWgsl = /* wgsl */ `
17
+ requires packed_4x8_integer_dot_product;
18
+
19
+ struct Params {
20
+ threads: u32,
21
+ iterations: u32,
22
+ };
23
+
24
+ @group(0) @binding(0) var<uniform> params: Params;
25
+ @group(0) @binding(1) var<storage, read_write> out: array<f32>;
26
+
27
+ // Hashes a seed into a u32 holding four packed int8 weights, exactly the
28
+ // storage format dot4I8Packed consumes.
29
+ fn weights(seed: u32) -> u32 {
30
+ return seed * 0x9e3779b1u;
31
+ }
32
+
33
+ @compute @workgroup_size(64)
34
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
35
+ let idx = gid.x;
36
+ if (idx >= params.threads) {
37
+ return;
38
+ }
39
+ let w00: u32 = weights(idx * 8u + 1u);
40
+ let w01: u32 = weights(idx * 8u + 2u);
41
+ let w10: u32 = weights(idx * 8u + 3u);
42
+ let w11: u32 = weights(idx * 8u + 4u);
43
+ let w20: u32 = weights(idx * 8u + 5u);
44
+ let w21: u32 = weights(idx * 8u + 6u);
45
+ let w30: u32 = weights(idx * 8u + 7u);
46
+ let w31: u32 = weights(idx * 8u + 8u);
47
+ var x0: u32 = (idx * 0x85ebca6bu) ^ 0x01020304u;
48
+ var x1: u32 = (idx * 0x85ebca6bu) ^ 0x02040608u;
49
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
50
+ let o0 = dot4I8Packed(w00, x0) + dot4I8Packed(w01, x1);
51
+ let o1 = dot4I8Packed(w10, x0) + dot4I8Packed(w11, x1);
52
+ let o2 = dot4I8Packed(w20, x0) + dot4I8Packed(w21, x1);
53
+ let o3 = dot4I8Packed(w30, x0) + dot4I8Packed(w31, x1);
54
+ x0 = x1;
55
+ x1 = pack4xI8(vec4<i32>(o0, o1, o2, o3) >> vec4<u32>(4u));
56
+ }
57
+ out[idx] = f32(i32(x0) + i32(x1));
58
+ }
59
+ `;
60
+ //# sourceMappingURL=flopsI8MatvecDp4a.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8MatvecDp4a.js","sourceRoot":"","sources":["../../src/shaders/flopsI8MatvecDp4a.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2C/C,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Raw int8-range FLOPS probe — same shape as {@link flopsF32ScalarWgsl}
3
+ * (eight independent chains, unrolled 4x), but on `i32` (WGSL has no
4
+ * first-class i8 type outside the packed-dot-product extension). Integer
5
+ * overflow wraps (defined two's-complement behavior in WGSL), so the chains
6
+ * stay bounded for any iteration count. 64 integer ops per loop iteration.
7
+ */
8
+ export declare const flopsI8ScalarWgsl = "\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: i32 = 3 + i32(idx & 15u);\n let b: i32 = 5 + i32(idx & 7u);\n var x0: i32 = i32(idx & 255u) + 1;\n var x1: i32 = i32(idx & 255u) + 2;\n var x2: i32 = i32(idx & 255u) + 3;\n var x3: i32 = i32(idx & 255u) + 4;\n var x4: i32 = i32(idx & 255u) + 5;\n var x5: i32 = i32(idx & 255u) + 6;\n var x6: i32 = i32(idx & 255u) + 7;\n var x7: i32 = i32(idx & 255u) + 8;\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";
9
+ //# sourceMappingURL=flopsI8Scalar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8Scalar.d.ts","sourceRoot":"","sources":["../../src/shaders/flopsI8Scalar.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,+gDA6D7B,CAAC"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Raw int8-range FLOPS probe — same shape as {@link flopsF32ScalarWgsl}
3
+ * (eight independent chains, unrolled 4x), but on `i32` (WGSL has no
4
+ * first-class i8 type outside the packed-dot-product extension). Integer
5
+ * overflow wraps (defined two's-complement behavior in WGSL), so the chains
6
+ * stay bounded for any iteration count. 64 integer ops per loop iteration.
7
+ */
8
+ export const flopsI8ScalarWgsl = /* wgsl */ `
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 a: i32 = 3 + i32(idx & 15u);
24
+ let b: i32 = 5 + i32(idx & 7u);
25
+ var x0: i32 = i32(idx & 255u) + 1;
26
+ var x1: i32 = i32(idx & 255u) + 2;
27
+ var x2: i32 = i32(idx & 255u) + 3;
28
+ var x3: i32 = i32(idx & 255u) + 4;
29
+ var x4: i32 = i32(idx & 255u) + 5;
30
+ var x5: i32 = i32(idx & 255u) + 6;
31
+ var x6: i32 = i32(idx & 255u) + 7;
32
+ var x7: i32 = i32(idx & 255u) + 8;
33
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
34
+ x0 = x0 * a + b;
35
+ x1 = x1 * a + b;
36
+ x2 = x2 * a + b;
37
+ x3 = x3 * a + b;
38
+ x4 = x4 * a + b;
39
+ x5 = x5 * a + b;
40
+ x6 = x6 * a + b;
41
+ x7 = x7 * a + b;
42
+ x0 = x0 * a + b;
43
+ x1 = x1 * a + b;
44
+ x2 = x2 * a + b;
45
+ x3 = x3 * a + b;
46
+ x4 = x4 * a + b;
47
+ x5 = x5 * a + b;
48
+ x6 = x6 * a + b;
49
+ x7 = x7 * a + b;
50
+ x0 = x0 * a + b;
51
+ x1 = x1 * a + b;
52
+ x2 = x2 * a + b;
53
+ x3 = x3 * a + b;
54
+ x4 = x4 * a + b;
55
+ x5 = x5 * a + b;
56
+ x6 = x6 * a + b;
57
+ x7 = x7 * a + b;
58
+ x0 = x0 * a + b;
59
+ x1 = x1 * a + b;
60
+ x2 = x2 * a + b;
61
+ x3 = x3 * a + b;
62
+ x4 = x4 * a + b;
63
+ x5 = x5 * a + b;
64
+ x6 = x6 * a + b;
65
+ x7 = x7 * a + b;
66
+ }
67
+ out[idx] = f32(x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7);
68
+ }
69
+ `;
70
+ //# sourceMappingURL=flopsI8Scalar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8Scalar.js","sourceRoot":"","sources":["../../src/shaders/flopsI8Scalar.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6D3C,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * int8-range vec4 FLOPS probe: a single integer FMA chain held in a
3
+ * `vec4<i32>` register (4 independent lanes per step; see the fp32 vec4
4
+ * probe for why this is ILP, not SIMD, on most GPUs).
5
+ */
6
+ export declare const flopsI8Vec4Wgsl = "\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: i32 = i32(idx & 15u);\n let a: vec4<i32> = vec4<i32>(3 + s, 5 + s, 7 + s, 9 + s);\n let b: vec4<i32> = vec4<i32>(11 - s, 13 - s, 17 - s, 19 - s);\n var x: vec4<i32> = vec4<i32>(i32(idx & 255u));\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=flopsI8Vec4.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8Vec4.d.ts","sourceRoot":"","sources":["../../src/shaders/flopsI8Vec4.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,eAAe,4qBAwB3B,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * int8-range vec4 FLOPS probe: a single integer FMA chain held in a
3
+ * `vec4<i32>` register (4 independent lanes per step; see the fp32 vec4
4
+ * probe for why this is ILP, not SIMD, on most GPUs).
5
+ */
6
+ export const flopsI8Vec4Wgsl = /* wgsl */ `
7
+ struct Params {
8
+ threads: u32,
9
+ iterations: u32,
10
+ };
11
+
12
+ @group(0) @binding(0) var<uniform> params: Params;
13
+ @group(0) @binding(1) var<storage, read_write> out: array<f32>;
14
+
15
+ @compute @workgroup_size(64)
16
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
17
+ let idx = gid.x;
18
+ if (idx >= params.threads) {
19
+ return;
20
+ }
21
+ let s: i32 = i32(idx & 15u);
22
+ let a: vec4<i32> = vec4<i32>(3 + s, 5 + s, 7 + s, 9 + s);
23
+ let b: vec4<i32> = vec4<i32>(11 - s, 13 - s, 17 - s, 19 - s);
24
+ var x: vec4<i32> = vec4<i32>(i32(idx & 255u));
25
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
26
+ x = x * a + b;
27
+ }
28
+ out[idx] = f32(x.x + x.y + x.z + x.w);
29
+ }
30
+ `;
31
+ //# sourceMappingURL=flopsI8Vec4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsI8Vec4.js","sourceRoot":"","sources":["../../src/shaders/flopsI8Vec4.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;CAwBzC,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Manual byte pack/unpack ops probe: no `pack4x*` (or `unpack4x*`) builtin, just
3
+ * the shift+mask bit-twiddling those builtins compile to, so this measures
4
+ * the raw integer ALU/shifter cost of doing it by hand. Eight independent
5
+ * u32 lanes, each per step: unpack 4 bytes (4 mask + 3 shift = 7 ops),
6
+ * increment+wrap each byte (4 add + 4 mask = 8 ops), repack (4 mask + 3
7
+ * shift + 3 or = 10 ops) = 25 ops/lane/step, no unrolling (each step is
8
+ * already many instructions deep). 200 ops per loop iteration.
9
+ */
10
+ export declare const flopsU32PackUnpackWgsl = "\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\nfn unpack4(v: u32) -> vec4<u32> {\n return vec4<u32>(v & 0xFFu, (v >> 8u) & 0xFFu, (v >> 16u) & 0xFFu, (v >> 24u) & 0xFFu);\n}\n\nfn pack4(b: vec4<u32>) -> u32 {\n return (b.x & 0xFFu) | ((b.y & 0xFFu) << 8u) | ((b.z & 0xFFu) << 16u) | ((b.w & 0xFFu) << 24u);\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 var v0: u32 = idx * 2654435761u;\n var v1: u32 = (idx + 1u) * 2654435761u;\n var v2: u32 = (idx + 2u) * 2654435761u;\n var v3: u32 = (idx + 3u) * 2654435761u;\n var v4: u32 = (idx + 4u) * 2654435761u;\n var v5: u32 = (idx + 5u) * 2654435761u;\n var v6: u32 = (idx + 6u) * 2654435761u;\n var v7: u32 = (idx + 7u) * 2654435761u;\n let one = vec4<u32>(1u);\n for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {\n v0 = pack4((unpack4(v0) + one) & vec4<u32>(0xFFu));\n v1 = pack4((unpack4(v1) + one) & vec4<u32>(0xFFu));\n v2 = pack4((unpack4(v2) + one) & vec4<u32>(0xFFu));\n v3 = pack4((unpack4(v3) + one) & vec4<u32>(0xFFu));\n v4 = pack4((unpack4(v4) + one) & vec4<u32>(0xFFu));\n v5 = pack4((unpack4(v5) + one) & vec4<u32>(0xFFu));\n v6 = pack4((unpack4(v6) + one) & vec4<u32>(0xFFu));\n v7 = pack4((unpack4(v7) + one) & vec4<u32>(0xFFu));\n }\n out[idx] = f32(v0 ^ v1 ^ v2 ^ v3 ^ v4 ^ v5 ^ v6 ^ v7);\n}\n";
11
+ //# sourceMappingURL=flopsU32PackUnpack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsU32PackUnpack.d.ts","sourceRoot":"","sources":["../../src/shaders/flopsU32PackUnpack.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,yhDA4ClC,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Manual byte pack/unpack ops probe: no `pack4x*` (or `unpack4x*`) builtin, just
3
+ * the shift+mask bit-twiddling those builtins compile to, so this measures
4
+ * the raw integer ALU/shifter cost of doing it by hand. Eight independent
5
+ * u32 lanes, each per step: unpack 4 bytes (4 mask + 3 shift = 7 ops),
6
+ * increment+wrap each byte (4 add + 4 mask = 8 ops), repack (4 mask + 3
7
+ * shift + 3 or = 10 ops) = 25 ops/lane/step, no unrolling (each step is
8
+ * already many instructions deep). 200 ops per loop iteration.
9
+ */
10
+ export const flopsU32PackUnpackWgsl = /* wgsl */ `
11
+ struct Params {
12
+ threads: u32,
13
+ iterations: u32,
14
+ };
15
+
16
+ @group(0) @binding(0) var<uniform> params: Params;
17
+ @group(0) @binding(1) var<storage, read_write> out: array<f32>;
18
+
19
+ fn unpack4(v: u32) -> vec4<u32> {
20
+ return vec4<u32>(v & 0xFFu, (v >> 8u) & 0xFFu, (v >> 16u) & 0xFFu, (v >> 24u) & 0xFFu);
21
+ }
22
+
23
+ fn pack4(b: vec4<u32>) -> u32 {
24
+ return (b.x & 0xFFu) | ((b.y & 0xFFu) << 8u) | ((b.z & 0xFFu) << 16u) | ((b.w & 0xFFu) << 24u);
25
+ }
26
+
27
+ @compute @workgroup_size(64)
28
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
29
+ let idx = gid.x;
30
+ if (idx >= params.threads) {
31
+ return;
32
+ }
33
+ var v0: u32 = idx * 2654435761u;
34
+ var v1: u32 = (idx + 1u) * 2654435761u;
35
+ var v2: u32 = (idx + 2u) * 2654435761u;
36
+ var v3: u32 = (idx + 3u) * 2654435761u;
37
+ var v4: u32 = (idx + 4u) * 2654435761u;
38
+ var v5: u32 = (idx + 5u) * 2654435761u;
39
+ var v6: u32 = (idx + 6u) * 2654435761u;
40
+ var v7: u32 = (idx + 7u) * 2654435761u;
41
+ let one = vec4<u32>(1u);
42
+ for (var i: u32 = 0u; i < params.iterations; i = i + 1u) {
43
+ v0 = pack4((unpack4(v0) + one) & vec4<u32>(0xFFu));
44
+ v1 = pack4((unpack4(v1) + one) & vec4<u32>(0xFFu));
45
+ v2 = pack4((unpack4(v2) + one) & vec4<u32>(0xFFu));
46
+ v3 = pack4((unpack4(v3) + one) & vec4<u32>(0xFFu));
47
+ v4 = pack4((unpack4(v4) + one) & vec4<u32>(0xFFu));
48
+ v5 = pack4((unpack4(v5) + one) & vec4<u32>(0xFFu));
49
+ v6 = pack4((unpack4(v6) + one) & vec4<u32>(0xFFu));
50
+ v7 = pack4((unpack4(v7) + one) & vec4<u32>(0xFFu));
51
+ }
52
+ out[idx] = f32(v0 ^ v1 ^ v2 ^ v3 ^ v4 ^ v5 ^ v6 ^ v7);
53
+ }
54
+ `;
55
+ //# sourceMappingURL=flopsU32PackUnpack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flopsU32PackUnpack.js","sourceRoot":"","sources":["../../src/shaders/flopsU32PackUnpack.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4ChD,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Read-bandwidth probe: each thread streams a run of `vec4<f32>` values out
3
+ * of a large storage buffer and folds them together with plain addition (no
4
+ * second buffer, no multiply) — the cheapest possible "prove you read it"
5
+ * op — then writes a single scalar. Reads dominate; writes are negligible
6
+ * (one f32 per thread vs. `cols4` vec4s read), so throughput here is close
7
+ * to the device's peak storage-buffer read bandwidth.
8
+ */
9
+ export declare const streamReadWgsl = "\nstruct Params {\n rows: u32,\n cols4: u32,\n};\n\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read> data: array<vec4<f32>>;\n@group(0) @binding(2) var<storage, read_write> out: array<f32>;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let row = gid.x;\n if (row >= params.rows) {\n return;\n }\n var sum: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);\n let base = row * params.cols4;\n for (var c: u32 = 0u; c < params.cols4; c = c + 1u) {\n sum = sum + data[base + c];\n }\n out[row] = sum.x + sum.y + sum.z + sum.w;\n}\n";
10
+ //# sourceMappingURL=streamRead.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamRead.d.ts","sourceRoot":"","sources":["../../src/shaders/streamRead.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,unBAuB1B,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Read-bandwidth probe: each thread streams a run of `vec4<f32>` values out
3
+ * of a large storage buffer and folds them together with plain addition (no
4
+ * second buffer, no multiply) — the cheapest possible "prove you read it"
5
+ * op — then writes a single scalar. Reads dominate; writes are negligible
6
+ * (one f32 per thread vs. `cols4` vec4s read), so throughput here is close
7
+ * to the device's peak storage-buffer read bandwidth.
8
+ */
9
+ export const streamReadWgsl = /* wgsl */ `
10
+ struct Params {
11
+ rows: u32,
12
+ cols4: u32,
13
+ };
14
+
15
+ @group(0) @binding(0) var<uniform> params: Params;
16
+ @group(0) @binding(1) var<storage, read> data: array<vec4<f32>>;
17
+ @group(0) @binding(2) var<storage, read_write> out: array<f32>;
18
+
19
+ @compute @workgroup_size(64)
20
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
21
+ let row = gid.x;
22
+ if (row >= params.rows) {
23
+ return;
24
+ }
25
+ var sum: vec4<f32> = vec4<f32>(0.0, 0.0, 0.0, 0.0);
26
+ let base = row * params.cols4;
27
+ for (var c: u32 = 0u; c < params.cols4; c = c + 1u) {
28
+ sum = sum + data[base + c];
29
+ }
30
+ out[row] = sum.x + sum.y + sum.z + sum.w;
31
+ }
32
+ `;
33
+ //# sourceMappingURL=streamRead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamRead.js","sourceRoot":"","sources":["../../src/shaders/streamRead.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;CAuBxC,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Write-bandwidth probe: each thread stores a run of `vec4<f32>` values into
3
+ * a large storage buffer. No buffer reads at all; the only input is the
4
+ * thread's own id, so throughput here is close to the device's peak
5
+ * storage-buffer write bandwidth.
6
+ */
7
+ export declare const streamWriteWgsl = "\nstruct Params {\n rows: u32,\n cols4: u32,\n};\n\n@group(0) @binding(0) var<uniform> params: Params;\n@group(0) @binding(1) var<storage, read_write> out: array<vec4<f32>>;\n\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) gid: vec3<u32>) {\n let row = gid.x;\n if (row >= params.rows) {\n return;\n }\n let base = row * params.cols4;\n let v = vec4<f32>(f32(row), f32(row), f32(row), f32(row));\n for (var c: u32 = 0u; c < params.cols4; c = c + 1u) {\n out[base + c] = v;\n }\n}\n";
8
+ //# sourceMappingURL=streamWrite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamWrite.d.ts","sourceRoot":"","sources":["../../src/shaders/streamWrite.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,eAAe,4gBAqB3B,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Write-bandwidth probe: each thread stores a run of `vec4<f32>` values into
3
+ * a large storage buffer. No buffer reads at all; the only input is the
4
+ * thread's own id, so throughput here is close to the device's peak
5
+ * storage-buffer write bandwidth.
6
+ */
7
+ export const streamWriteWgsl = /* wgsl */ `
8
+ struct Params {
9
+ rows: u32,
10
+ cols4: u32,
11
+ };
12
+
13
+ @group(0) @binding(0) var<uniform> params: Params;
14
+ @group(0) @binding(1) var<storage, read_write> out: array<vec4<f32>>;
15
+
16
+ @compute @workgroup_size(64)
17
+ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
18
+ let row = gid.x;
19
+ if (row >= params.rows) {
20
+ return;
21
+ }
22
+ let base = row * params.cols4;
23
+ let v = vec4<f32>(f32(row), f32(row), f32(row), f32(row));
24
+ for (var c: u32 = 0u; c < params.cols4; c = c + 1u) {
25
+ out[base + c] = v;
26
+ }
27
+ }
28
+ `;
29
+ //# sourceMappingURL=streamWrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"streamWrite.js","sourceRoot":"","sources":["../../src/shaders/streamWrite.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;CAqBzC,CAAC"}
@@ -0,0 +1,31 @@
1
+ import type { Stats } from './types.ts';
2
+ /** Student-t multiplier for a two-sided 95% confidence interval with `df` degrees of freedom. */
3
+ export declare function tQuantile975(df: number): number;
4
+ export declare function computeStats(values: readonly number[]): Stats;
5
+ export interface BestStableOptions {
6
+ /** Never declare the best stable with fewer samples than this. */
7
+ minRuns: number;
8
+ /** The best must have gone this many consecutive samples without a meaningful improvement. */
9
+ stableRuns: number;
10
+ /** A new best more than this fraction below the previous best counts as an improvement (0.01 = 1%). */
11
+ tolerance: number;
12
+ }
13
+ /**
14
+ * Stopping rule for best-of-N sampling.
15
+ *
16
+ * The reported statistic is the *minimum* time, since every source of noise a
17
+ * benchmark meets (thermal throttling, clock ramp, compositor frames, other
18
+ * apps) only ever makes a run slower — the fastest run is the closest thing
19
+ * to the device's true capability. So sampling is done once the minimum has
20
+ * settled: the last `stableRuns` samples failed to beat the best seen before
21
+ * them by more than `tolerance`. Three tight samples converge immediately
22
+ * (minRuns 3, stableRuns 2: sample 1 sets the best, samples 2 and 3 confirm
23
+ * it); a still-ramping GPU that keeps producing faster runs keeps sampling.
24
+ *
25
+ * Degenerate data (non-positive or non-finite best) never converges, so the
26
+ * caller runs to its cap and can then diagnose the bad timings itself.
27
+ */
28
+ export declare function isBestStable(values: readonly number[], opts: BestStableOptions): boolean;
29
+ /** True when `sampleMs` is more than `threshold` (fraction) slower than `bestMs` — the signature of a throttled run. */
30
+ export declare function isThrottled(sampleMs: number, bestMs: number, threshold: number): boolean;
31
+ //# sourceMappingURL=stats.d.ts.map