@graphty/webgpu-graph-algorithms 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -52
- package/dist/browser.js +1 -1
- package/dist/chunks/{context-CRbw2Wyo.js → context-BXqgCifx.js} +225 -33
- package/dist/chunks/context-BXqgCifx.js.map +1 -0
- package/dist/node.js +1 -1
- package/dist/src/accelerator.d.ts +12 -10
- package/dist/src/accelerator.d.ts.map +1 -1
- package/dist/src/accelerator.js +32 -10
- package/dist/src/accelerator.js.map +1 -1
- package/dist/src/algorithms/components.d.ts.map +1 -1
- package/dist/src/algorithms/components.js +12 -13
- package/dist/src/algorithms/components.js.map +1 -1
- package/dist/src/algorithms/degree.d.ts +6 -8
- package/dist/src/algorithms/degree.d.ts.map +1 -1
- package/dist/src/algorithms/degree.js +58 -35
- package/dist/src/algorithms/degree.js.map +1 -1
- package/dist/src/algorithms/pagerank.d.ts.map +1 -1
- package/dist/src/algorithms/pagerank.js +16 -14
- package/dist/src/algorithms/pagerank.js.map +1 -1
- package/dist/src/algorithms/power-iteration.d.ts +2 -2
- package/dist/src/algorithms/power-iteration.d.ts.map +1 -1
- package/dist/src/algorithms/power-iteration.js +17 -14
- package/dist/src/algorithms/power-iteration.js.map +1 -1
- package/dist/src/constants.d.ts +85 -8
- package/dist/src/constants.d.ts.map +1 -1
- package/dist/src/constants.js +85 -8
- package/dist/src/constants.js.map +1 -1
- package/dist/src/errors.d.ts +3 -2
- package/dist/src/errors.d.ts.map +1 -1
- package/dist/src/errors.js +2 -1
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +10 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +14 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/dispatch.d.ts +8 -3
- package/dist/src/kernel/dispatch.d.ts.map +1 -1
- package/dist/src/kernel/dispatch.js +18 -7
- package/dist/src/kernel/dispatch.js.map +1 -1
- package/dist/src/kernel/kernel.d.ts +30 -1
- package/dist/src/kernel/kernel.d.ts.map +1 -1
- package/dist/src/kernel/kernel.js +49 -5
- package/dist/src/kernel/kernel.js.map +1 -1
- package/dist/src/kernel/prelude.d.ts.map +1 -1
- package/dist/src/kernel/prelude.js +9 -1
- package/dist/src/kernel/prelude.js.map +1 -1
- package/dist/src/kernel/profiler.d.ts +15 -3
- package/dist/src/kernel/profiler.d.ts.map +1 -1
- package/dist/src/kernel/profiler.js +27 -4
- package/dist/src/kernel/profiler.js.map +1 -1
- package/dist/src/kernels.d.ts +18 -8
- package/dist/src/kernels.d.ts.map +1 -1
- package/dist/src/kernels.js +345 -22
- package/dist/src/kernels.js.map +1 -1
- package/dist/src/layouts/calibrate.d.ts +51 -0
- package/dist/src/layouts/calibrate.d.ts.map +1 -0
- package/dist/src/layouts/calibrate.js +172 -0
- package/dist/src/layouts/calibrate.js.map +1 -0
- package/dist/src/layouts/force-simulation.d.ts +42 -5
- package/dist/src/layouts/force-simulation.d.ts.map +1 -1
- package/dist/src/layouts/force-simulation.js +84 -22
- package/dist/src/layouts/force-simulation.js.map +1 -1
- package/dist/src/layouts/forceatlas2.d.ts +107 -38
- package/dist/src/layouts/forceatlas2.d.ts.map +1 -1
- package/dist/src/layouts/forceatlas2.js +297 -290
- package/dist/src/layouts/forceatlas2.js.map +1 -1
- package/dist/src/layouts/fruchterman-reingold.d.ts +241 -0
- package/dist/src/layouts/fruchterman-reingold.d.ts.map +1 -0
- package/dist/src/layouts/fruchterman-reingold.js +739 -0
- package/dist/src/layouts/fruchterman-reingold.js.map +1 -0
- package/dist/src/layouts/model-common.d.ts +140 -0
- package/dist/src/layouts/model-common.d.ts.map +1 -0
- package/dist/src/layouts/model-common.js +269 -0
- package/dist/src/layouts/model-common.js.map +1 -0
- package/dist/src/layouts/repulsion-grid.d.ts +152 -0
- package/dist/src/layouts/repulsion-grid.d.ts.map +1 -0
- package/dist/src/layouts/repulsion-grid.js +318 -0
- package/dist/src/layouts/repulsion-grid.js.map +1 -0
- package/dist/src/layouts/spring-electrical.d.ts +224 -0
- package/dist/src/layouts/spring-electrical.d.ts.map +1 -0
- package/dist/src/layouts/spring-electrical.js +665 -0
- package/dist/src/layouts/spring-electrical.js.map +1 -0
- package/dist/src/memory/residency.d.ts +6 -2
- package/dist/src/memory/residency.d.ts.map +1 -1
- package/dist/src/memory/residency.js +84 -14
- package/dist/src/memory/residency.js.map +1 -1
- package/dist/src/primitives/core-shape.d.ts +38 -2
- package/dist/src/primitives/core-shape.d.ts.map +1 -1
- package/dist/src/primitives/core-shape.js +71 -3
- package/dist/src/primitives/core-shape.js.map +1 -1
- package/dist/src/primitives/grid-pyramid.d.ts +71 -0
- package/dist/src/primitives/grid-pyramid.d.ts.map +1 -0
- package/dist/src/primitives/grid-pyramid.js +143 -0
- package/dist/src/primitives/grid-pyramid.js.map +1 -0
- package/dist/src/primitives/grid.d.ts +118 -0
- package/dist/src/primitives/grid.d.ts.map +1 -0
- package/dist/src/primitives/grid.js +225 -0
- package/dist/src/primitives/grid.js.map +1 -0
- package/dist/src/primitives/histogram.d.ts +67 -0
- package/dist/src/primitives/histogram.d.ts.map +1 -0
- package/dist/src/primitives/histogram.js +190 -0
- package/dist/src/primitives/histogram.js.map +1 -0
- package/dist/src/primitives/radix-sort.d.ts +75 -0
- package/dist/src/primitives/radix-sort.d.ts.map +1 -0
- package/dist/src/primitives/radix-sort.js +168 -0
- package/dist/src/primitives/radix-sort.js.map +1 -0
- package/dist/src/primitives/scan.d.ts +44 -0
- package/dist/src/primitives/scan.d.ts.map +1 -0
- package/dist/src/primitives/scan.js +151 -0
- package/dist/src/primitives/scan.js.map +1 -0
- package/dist/src/primitives/segmented-reduce.d.ts +25 -17
- package/dist/src/primitives/segmented-reduce.d.ts.map +1 -1
- package/dist/src/primitives/segmented-reduce.js +166 -47
- package/dist/src/primitives/segmented-reduce.js.map +1 -1
- package/dist/src/primitives/spmv.d.ts +18 -14
- package/dist/src/primitives/spmv.d.ts.map +1 -1
- package/dist/src/primitives/spmv.js +94 -58
- package/dist/src/primitives/spmv.js.map +1 -1
- package/dist/src/primitives/verify.d.ts +49 -0
- package/dist/src/primitives/verify.d.ts.map +1 -0
- package/dist/src/primitives/verify.js +229 -0
- package/dist/src/primitives/verify.js.map +1 -0
- package/dist/src/types/accelerator.d.ts +7 -3
- package/dist/src/types/accelerator.d.ts.map +1 -1
- package/dist/src/types/context.d.ts +53 -0
- package/dist/src/types/context.d.ts.map +1 -1
- package/dist/src/types/layout.d.ts +52 -0
- package/dist/src/types/layout.d.ts.map +1 -1
- package/dist/src/types/options.d.ts +43 -1
- package/dist/src/types/options.d.ts.map +1 -1
- package/dist/src/wgsl/counting-scatter.wgsl.d.ts +8 -0
- package/dist/src/wgsl/counting-scatter.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/counting-scatter.wgsl.js +17 -0
- package/dist/src/wgsl/counting-scatter.wgsl.js.map +1 -0
- package/dist/src/wgsl/fa2-attraction.wgsl.d.ts +23 -8
- package/dist/src/wgsl/fa2-attraction.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-attraction.wgsl.js +100 -17
- package/dist/src/wgsl/fa2-attraction.wgsl.js.map +1 -1
- package/dist/src/wgsl/fa2-integrate.wgsl.d.ts +7 -2
- package/dist/src/wgsl/fa2-integrate.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-integrate.wgsl.js +28 -2
- package/dist/src/wgsl/fa2-integrate.wgsl.js.map +1 -1
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts +4 -2
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js +14 -5
- package/dist/src/wgsl/fa2-repulsion-exact.wgsl.js.map +1 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts +12 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.js +54 -0
- package/dist/src/wgsl/fa2-stats-finalize.wgsl.js.map +1 -1
- package/dist/src/wgsl/grid-cell-key.wgsl.d.ts +8 -0
- package/dist/src/wgsl/grid-cell-key.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-cell-key.wgsl.js +30 -0
- package/dist/src/wgsl/grid-cell-key.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.d.ts +8 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.js +29 -0
- package/dist/src/wgsl/grid-centroid-hub.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-centroid.wgsl.d.ts +8 -0
- package/dist/src/wgsl/grid-centroid.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-centroid.wgsl.js +29 -0
- package/dist/src/wgsl/grid-centroid.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-downsample.wgsl.d.ts +7 -0
- package/dist/src/wgsl/grid-downsample.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-downsample.wgsl.js +28 -0
- package/dist/src/wgsl/grid-downsample.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-far-field.wgsl.d.ts +13 -0
- package/dist/src/wgsl/grid-far-field.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-far-field.wgsl.js +98 -0
- package/dist/src/wgsl/grid-far-field.wgsl.js.map +1 -0
- package/dist/src/wgsl/grid-near-field.wgsl.d.ts +19 -0
- package/dist/src/wgsl/grid-near-field.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/grid-near-field.wgsl.js +129 -0
- package/dist/src/wgsl/grid-near-field.wgsl.js.map +1 -0
- package/dist/src/wgsl/histogram.wgsl.d.ts +7 -0
- package/dist/src/wgsl/histogram.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/histogram.wgsl.js +15 -0
- package/dist/src/wgsl/histogram.wgsl.js.map +1 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.d.ts +8 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.js +26 -0
- package/dist/src/wgsl/indirect-finalize.wgsl.js.map +1 -0
- package/dist/src/wgsl/radix-hist.wgsl.d.ts +9 -0
- package/dist/src/wgsl/radix-hist.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/radix-hist.wgsl.js +31 -0
- package/dist/src/wgsl/radix-hist.wgsl.js.map +1 -0
- package/dist/src/wgsl/radix-scatter.wgsl.d.ts +9 -0
- package/dist/src/wgsl/radix-scatter.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/radix-scatter.wgsl.js +40 -0
- package/dist/src/wgsl/radix-scatter.wgsl.js.map +1 -0
- package/dist/src/wgsl/scan-add.wgsl.d.ts +6 -0
- package/dist/src/wgsl/scan-add.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/scan-add.wgsl.js +14 -0
- package/dist/src/wgsl/scan-add.wgsl.js.map +1 -0
- package/dist/src/wgsl/scan-block.wgsl.d.ts +8 -0
- package/dist/src/wgsl/scan-block.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/scan-block.wgsl.js +30 -0
- package/dist/src/wgsl/scan-block.wgsl.js.map +1 -0
- package/dist/src/wgsl/segmented-reduce.wgsl.d.ts +22 -8
- package/dist/src/wgsl/segmented-reduce.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/segmented-reduce.wgsl.js +84 -15
- package/dist/src/wgsl/segmented-reduce.wgsl.js.map +1 -1
- package/dist/src/wgsl/spmv-pull.wgsl.d.ts +22 -11
- package/dist/src/wgsl/spmv-pull.wgsl.d.ts.map +1 -1
- package/dist/src/wgsl/spmv-pull.wgsl.js +110 -36
- package/dist/src/wgsl/spmv-pull.wgsl.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/webgpu-graph-algorithms.js +5016 -1130
- package/dist/webgpu-graph-algorithms.js.map +1 -1
- package/package.json +10 -7
- package/src/accelerator.ts +46 -12
- package/src/algorithms/components.ts +12 -16
- package/src/algorithms/degree.ts +58 -43
- package/src/algorithms/pagerank.ts +20 -18
- package/src/algorithms/power-iteration.ts +19 -18
- package/src/constants.ts +108 -8
- package/src/errors.ts +3 -1
- package/src/index.ts +25 -5
- package/src/kernel/dispatch.ts +18 -7
- package/src/kernel/kernel.ts +59 -5
- package/src/kernel/prelude.ts +15 -0
- package/src/kernel/profiler.ts +28 -4
- package/src/kernels.ts +378 -24
- package/src/layouts/calibrate.ts +187 -0
- package/src/layouts/force-simulation.ts +111 -26
- package/src/layouts/forceatlas2.ts +346 -324
- package/src/layouts/fruchterman-reingold.ts +918 -0
- package/src/layouts/model-common.ts +323 -0
- package/src/layouts/repulsion-grid.ts +451 -0
- package/src/layouts/spring-electrical.ts +845 -0
- package/src/memory/residency.ts +126 -20
- package/src/primitives/core-shape.ts +91 -4
- package/src/primitives/grid-pyramid.ts +221 -0
- package/src/primitives/grid.ts +349 -0
- package/src/primitives/histogram.ts +273 -0
- package/src/primitives/radix-sort.ts +246 -0
- package/src/primitives/scan.ts +197 -0
- package/src/primitives/segmented-reduce.ts +214 -56
- package/src/primitives/spmv.ts +125 -65
- package/src/primitives/verify.ts +249 -0
- package/src/types/accelerator.ts +15 -3
- package/src/types/context.ts +56 -0
- package/src/types/layout.ts +58 -0
- package/src/types/options.ts +45 -1
- package/src/wgsl/counting-scatter.wgsl.ts +16 -0
- package/src/wgsl/fa2-attraction.wgsl.ts +100 -17
- package/src/wgsl/fa2-integrate.wgsl.ts +28 -2
- package/src/wgsl/fa2-repulsion-exact.wgsl.ts +14 -5
- package/src/wgsl/fa2-stats-finalize.wgsl.ts +54 -0
- package/src/wgsl/grid-cell-key.wgsl.ts +29 -0
- package/src/wgsl/grid-centroid-hub.wgsl.ts +28 -0
- package/src/wgsl/grid-centroid.wgsl.ts +28 -0
- package/src/wgsl/grid-downsample.wgsl.ts +27 -0
- package/src/wgsl/grid-far-field.wgsl.ts +97 -0
- package/src/wgsl/grid-near-field.wgsl.ts +128 -0
- package/src/wgsl/histogram.wgsl.ts +14 -0
- package/src/wgsl/indirect-finalize.wgsl.ts +25 -0
- package/src/wgsl/radix-hist.wgsl.ts +30 -0
- package/src/wgsl/radix-scatter.wgsl.ts +39 -0
- package/src/wgsl/scan-add.wgsl.ts +13 -0
- package/src/wgsl/scan-block.wgsl.ts +29 -0
- package/src/wgsl/segmented-reduce.wgsl.ts +84 -15
- package/src/wgsl/spmv-pull.wgsl.ts +110 -36
- package/dist/chunks/context-CRbw2Wyo.js.map +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `scan-add` kernel body (spec 6 row 2 step (c); P4-T2): adds the exclusive prefix of its block's sum,
|
|
3
|
+
* `blockOffsets[group]`, to every element of the block. Body only; normative text.
|
|
4
|
+
*/
|
|
5
|
+
export const scanAddWgsl = /* wgsl */ `
|
|
6
|
+
@compute @workgroup_size(WG)
|
|
7
|
+
fn scan_add(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
8
|
+
let g = group_id(wid);
|
|
9
|
+
let i = g * WG + lid.x;
|
|
10
|
+
if (i >= P.count) { return; } // no barrier follows
|
|
11
|
+
out[i] = out[i] + blockOffsets[g];
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `scan-block` kernel body (spec 6 row 2; P4-T2): an exclusive prefix sum of one WG-wide block of `src` in
|
|
3
|
+
* workgroup memory (Hillis-Steele, log2(WG) rounds of two barriers each, every lane in uniform control flow) into
|
|
4
|
+
* `out`, and the block's inclusive total into `blockSums[group]`. u32 addition is exact in any order, so the
|
|
5
|
+
* output is bitwise the same on every adapter (PD-3). Body only (spec 3.5, D9); normative text.
|
|
6
|
+
*/
|
|
7
|
+
export const scanBlockWgsl = /* wgsl */ `
|
|
8
|
+
var<workgroup> sh: array<u32, WG>;
|
|
9
|
+
|
|
10
|
+
@compute @workgroup_size(WG)
|
|
11
|
+
fn scan_block(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
12
|
+
let g = group_id(wid);
|
|
13
|
+
let i = g * WG + lid.x;
|
|
14
|
+
var v = 0u;
|
|
15
|
+
if (i < P.count) { v = src[i]; }
|
|
16
|
+
sh[lid.x] = v;
|
|
17
|
+
workgroupBarrier();
|
|
18
|
+
for (var s = 1u; s < WG; s = s * 2u) { // Hillis-Steele inclusive scan; uniform: every lane runs every round
|
|
19
|
+
var t = 0u;
|
|
20
|
+
if (lid.x >= s) { t = sh[lid.x - s]; }
|
|
21
|
+
workgroupBarrier();
|
|
22
|
+
sh[lid.x] = sh[lid.x] + t;
|
|
23
|
+
workgroupBarrier();
|
|
24
|
+
}
|
|
25
|
+
let inclusive = sh[lid.x];
|
|
26
|
+
if (i < P.count) { out[i] = inclusive - v; } // exclusive = inclusive - own value
|
|
27
|
+
if (lid.x == WG - 1u) { blockSums[g] = inclusive; } // the block total (the last lane's inclusive sum)
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
@@ -1,28 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `segmented-reduce` kernel body (spec 6 row 3; contract 4.5)
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* The `segmented-reduce` kernel body (spec 6 row 3; contract 4.5) in its three degree tiers (P4-T5, PD-6). Every
|
|
3
|
+
* tier folds the VALUE snippet over a row's arcs inside the bound window [P.arcBase, P.arcEnd) and writes out[i]
|
|
4
|
+
* as f32 (a row with no arcs gets the identity element: 0 for sum, F32_MAX for min, -F32_MAX for max); the row is
|
|
5
|
+
* `perm[row]` under USE_PERM and `row` otherwise, over the rows [P.start, P.end) of the dispatch. TIER 0 is one row
|
|
6
|
+
* per thread (no barrier, so its early return is legal); TIER 1 is 32 lanes per row and WG / 32 rows per workgroup,
|
|
7
|
+
* reduced by a five-step tree in workgroup memory (bitwise the same on every subgroup size); TIER 2 is one row per
|
|
8
|
+
* workgroup through `wg_reduce_f32` of the prelude (the subgroup variant when the feature exists, the workgroup twin
|
|
9
|
+
* otherwise -- hence `needs: ["subgroups"]`). The tier bodies are functions called under `if (TIER == 0u)`, an
|
|
10
|
+
* override, so every barrier is reached in uniform control flow. The body is normative: a sabotage mutation
|
|
11
|
+
* (test/helpers/sabotage.ts) is a textual edit of it, so it is not restyled.
|
|
12
|
+
*
|
|
13
|
+
* TIER 0 folds its row through `row_fold_dense`, a stride-one copy of `row_fold`, because the shader compiler emits
|
|
14
|
+
* `row_fold(i, 0u, 1u)` as a call and leaves the stride in a parameter: the loop then walks the row with a runtime
|
|
15
|
+
* step, which costs the strength-reduced addressing into colIdx / weights and the unrolling that keeps several loads
|
|
16
|
+
* in flight per thread. PageRank's out-weight pass, the only caller that passes no tiers today, runs TIER 0 alone,
|
|
17
|
+
* and under the tiers TIER 0 still folds the low-degree rows, which are most of them. The two folds spell their
|
|
18
|
+
* locals apart (`lo` / `hi` / `k` against `a0` / `a1`) so that each sabotage row names exactly one of them; the
|
|
19
|
+
* VALUE snippet sees the same `row`, `arc`, `nbr` and `weight` in both.
|
|
7
20
|
*/
|
|
8
21
|
|
|
9
22
|
/**
|
|
10
|
-
* Entry point `segmented_reduce`; overrides OP (0 sum, 1 min, 2 max) and TIER (0
|
|
11
|
-
* assigning `v` from `row`, `arc`, `nbr`, `weight` (`target`
|
|
23
|
+
* Entry point `segmented_reduce`; overrides OP (0 sum, 1 min, 2 max) and TIER (0 thread-per-row, 1 32-lanes-per-row,
|
|
24
|
+
* 2 workgroup-per-row); snippet slot VALUE: statements assigning `v` from `row`, `arc`, `nbr`, `weight` (`target`
|
|
25
|
+
* is a WGSL reserved word, hence `nbr`).
|
|
12
26
|
*/
|
|
13
27
|
export const segmentedReduceWgsl = /* wgsl */ `
|
|
14
28
|
fn identity() -> f32 { if (OP == 1u) { return F32_MAX; } if (OP == 2u) { return -F32_MAX; } return 0.0; }
|
|
15
29
|
fn comb(a: f32, b: f32) -> f32 { if (OP == 1u) { return min(a, b); } if (OP == 2u) { return max(a, b); } return a + b; }
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let row = linear_id(wid, lid.x) + P.start;
|
|
20
|
-
if (row >= P.end) { return; }
|
|
21
|
-
let i = select(row, perm[row], USE_PERM);
|
|
30
|
+
fn row_node(row: u32) -> u32 { return select(row, perm[row], USE_PERM); }
|
|
31
|
+
fn row_fold(i: u32, lane: u32, step: u32) -> f32 { // the arcs of row i this lane walks inside the bound window [P.arcBase, P.arcEnd)
|
|
32
|
+
let row = i; // the CSR row the VALUE snippet may name (the node index, under USE_PERM too)
|
|
22
33
|
let a0 = max(rowPtr[i], P.arcBase);
|
|
23
34
|
let a1 = min(rowPtr[i + 1u], P.arcEnd);
|
|
24
35
|
var acc = identity();
|
|
25
|
-
for (var arc = a0; arc < a1; arc = arc +
|
|
36
|
+
for (var arc = a0 + lane; arc < a1; arc = arc + step) {
|
|
26
37
|
let nbr = colIdx[arc - P.arcBase]; // the neighbour index (\`target\` is a WGSL reserved word)
|
|
27
38
|
var weight = 1.0;
|
|
28
39
|
if (HAS_WEIGHTS) { weight = weights[arc - P.arcBase]; }
|
|
@@ -30,6 +41,64 @@ fn segmented_reduce(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invoca
|
|
|
30
41
|
//@@VALUE@@
|
|
31
42
|
acc = comb(acc, v);
|
|
32
43
|
}
|
|
33
|
-
|
|
44
|
+
return acc;
|
|
45
|
+
}
|
|
46
|
+
fn row_fold_dense(i: u32) -> f32 { // TIER 0's stride-one twin of row_fold (see the header)
|
|
47
|
+
let row = i; // the CSR row the VALUE snippet may name (as in row_fold)
|
|
48
|
+
let lo = max(rowPtr[i], P.arcBase);
|
|
49
|
+
let hi = min(rowPtr[i + 1u], P.arcEnd);
|
|
50
|
+
var acc = identity();
|
|
51
|
+
for (var arc = lo; arc < hi; arc = arc + 1u) {
|
|
52
|
+
let k = arc - P.arcBase; // the window-local index; this walk is contiguous
|
|
53
|
+
let nbr = colIdx[k];
|
|
54
|
+
var weight = 1.0;
|
|
55
|
+
if (HAS_WEIGHTS) { weight = weights[k]; }
|
|
56
|
+
var v = 0.0;
|
|
57
|
+
//@@VALUE@@
|
|
58
|
+
acc = comb(acc, v);
|
|
59
|
+
}
|
|
60
|
+
return acc;
|
|
61
|
+
}
|
|
62
|
+
fn finish(i: u32, acc: f32) { out[i] = select(acc, comb(out[i], acc), P.accumulate == 1u); }
|
|
63
|
+
fn tier0(wid: vec3<u32>, lane: u32) { // TIER 0: one row per thread over [P.start, P.end); no barrier, so the early return is legal (3.5 rule 1)
|
|
64
|
+
let row = linear_id(wid, lane) + P.start;
|
|
65
|
+
if (row >= P.end) { return; }
|
|
66
|
+
let i = row_node(row);
|
|
67
|
+
finish(i, row_fold_dense(i));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var<workgroup> sh: array<f32, WG>;
|
|
71
|
+
|
|
72
|
+
fn tiered(wid: vec3<u32>, lid: u32) { // TIER 1: 32 lanes per row, WG / 32 rows per workgroup; TIER 2: WG lanes per row (PD-6)
|
|
73
|
+
let g = group_id(wid);
|
|
74
|
+
var row = P.start + g;
|
|
75
|
+
var lane = lid;
|
|
76
|
+
var step = WG;
|
|
77
|
+
if (TIER == 1u) { row = P.start + g * (WG / 32u) + lid / 32u; lane = lid % 32u; step = 32u; }
|
|
78
|
+
let valid = row < P.end;
|
|
79
|
+
var i = 0u;
|
|
80
|
+
var acc = identity();
|
|
81
|
+
if (valid) { i = row_node(row); acc = row_fold(i, lane, step); }
|
|
82
|
+
if (TIER == 1u) {
|
|
83
|
+
sh[lid] = acc;
|
|
84
|
+
workgroupBarrier();
|
|
85
|
+
for (var s = 16u; s >= 1u; s = s / 2u) { // the five-step tree over each 32-lane group; every lane runs every step
|
|
86
|
+
var t = identity();
|
|
87
|
+
if (lane < s) { t = sh[lid + s]; }
|
|
88
|
+
workgroupBarrier();
|
|
89
|
+
sh[lid] = comb(sh[lid], t);
|
|
90
|
+
workgroupBarrier();
|
|
91
|
+
}
|
|
92
|
+
if (valid && lane == 0u) { finish(i, sh[lid]); }
|
|
93
|
+
}
|
|
94
|
+
if (TIER == 2u) {
|
|
95
|
+
let t = wg_reduce_f32(acc, lid, OP); // the workgroup tree of the prelude (subgroup variant when available)
|
|
96
|
+
if (valid && lid == 0u) { finish(i, t); }
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@compute @workgroup_size(WG)
|
|
101
|
+
fn segmented_reduce(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
102
|
+
if (TIER == 0u) { tier0(wid, lid.x); } else { tiered(wid, lid.x); }
|
|
34
103
|
}
|
|
35
104
|
`;
|
|
@@ -1,47 +1,121 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `spmv-pull` kernel body (spec 6 row 9, 8.2; PD-1 of the M8b plan)
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* The `spmv-pull` kernel body (spec 6 row 9, 8.2; PD-1 of the M8b plan) in its three in-degree tiers (P4-T5, PD-6):
|
|
3
|
+
* every tier folds `weight * xNorm[nbr]` over a row's in-arcs of the REVERSE adjacency in chunks of 64 terms (a
|
|
4
|
+
* two-level f32 sum: the chunk absorbs the rounding of 64 terms, the row total the rounding of the chunk count, so a
|
|
5
|
+
* 10,000-arc hub row loses about 200 rounding steps instead of 10,000; Kahan compensation is not used because
|
|
6
|
+
* Metal's shader compiler folds `((acc + term) - acc) - term` to zero whatever hides it) and writes
|
|
7
7
|
* `rankOut[v] = beta * pv + alpha * (sum + danglingMass * pv)`, where `pv` is `personalization[v]` when
|
|
8
|
-
* HAS_PERSONALIZATION and the uniform `P.uniformP` otherwise.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* HAS_PERSONALIZATION and the uniform `P.uniformP` otherwise. The rows are [P.start, P.n) of the dispatch, `perm[row]`
|
|
9
|
+
* under USE_PERM: TIER 0 is grid-stride over them (one row per thread, `P.stride` from planGridStride); TIER 1 is 32
|
|
10
|
+
* lanes per row and WG / 32 rows per workgroup with a five-step tree in workgroup memory (each lane keeps its own
|
|
11
|
+
* 64-term chunking); TIER 2 is one row per workgroup through `wg_reduce_f32` (hence `needs: ["subgroups"]`). PageRank
|
|
12
|
+
* sets alpha to the damping factor, beta to `1 - alpha` and USE_DANGLING; HITS and eigenvector set alpha 1, beta 0,
|
|
13
|
+
* uniformP 0; Katz sets alpha to the attenuation, beta to its constant and uniformP 1. The body is normative: a
|
|
14
|
+
* sabotage mutation (test/helpers/sabotage.ts) is a textual edit of it, so it is not restyled.
|
|
15
|
+
*
|
|
16
|
+
* TIER 0 folds its row through `row_sum_dense`, a stride-one copy of `row_sum`, because the shader compiler emits
|
|
17
|
+
* `row_sum(v, 0u, 1u)` as a call and leaves the stride in a parameter: the loop then walks the row with a runtime
|
|
18
|
+
* step, which costs the strength-reduced addressing into colIdx / weights and the unrolling that keeps several loads
|
|
19
|
+
* in flight per thread. PageRank and every other caller that passes no in-degree tiers runs TIER 0 alone, and under
|
|
20
|
+
* the tiers it still folds the low-degree rows, which are most of them; the shared fold cost 38 per cent more GPU
|
|
21
|
+
* time at 100k nodes and 1M arcs on an RTX 4070 SUPER and about twice the time on a Tesla T4. The two folds spell
|
|
22
|
+
* their locals apart (`lo` / `hi` / `k` against `a0` / `a1`) so that each sabotage row names exactly one of them.
|
|
12
23
|
*/
|
|
13
24
|
|
|
14
|
-
/** Entry point `spmv_pull`; overrides HAS_PERSONALIZATION and
|
|
25
|
+
/** Entry point `spmv_pull`; overrides HAS_PERSONALIZATION, USE_DANGLING and TIER (0 / 1 / 2) plus the standard USE_PERM / HAS_WEIGHTS. */
|
|
15
26
|
export const spmvPullWgsl = /* wgsl */ `
|
|
16
|
-
|
|
17
|
-
fn
|
|
27
|
+
fn row_node(row: u32) -> u32 { return select(row, perm[row], USE_PERM); }
|
|
28
|
+
fn row_sum(v: u32, lane: u32, step: u32) -> f32 { // this lane's arcs of row v inside the bound window, the two-level 64-term fold of the header
|
|
29
|
+
let a0 = max(rowPtr[v], P.arcBase);
|
|
30
|
+
let a1 = min(rowPtr[v + 1u], P.arcEnd);
|
|
31
|
+
var acc = 0.0;
|
|
32
|
+
var chunk = 0.0;
|
|
33
|
+
var inChunk = 0u;
|
|
34
|
+
for (var arc = a0 + lane; arc < a1; arc = arc + step) {
|
|
35
|
+
let nbr = colIdx[arc - P.arcBase]; // \`target\` is a WGSL reserved word (spec 16.2)
|
|
36
|
+
var weight = 1.0;
|
|
37
|
+
if (HAS_WEIGHTS) { weight = weights[arc - P.arcBase]; }
|
|
38
|
+
// two-level sum: 64 terms into chunk, chunk into acc (see the header; no compensation, no select)
|
|
39
|
+
chunk = chunk + (weight * xNorm[nbr]);
|
|
40
|
+
inChunk = inChunk + 1u;
|
|
41
|
+
if (inChunk == 64u) {
|
|
42
|
+
acc = acc + chunk;
|
|
43
|
+
chunk = 0.0;
|
|
44
|
+
inChunk = 0u;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
acc = acc + chunk;
|
|
48
|
+
return acc;
|
|
49
|
+
}
|
|
50
|
+
fn row_sum_dense(v: u32) -> f32 { // TIER 0's stride-one twin of row_sum (see the header)
|
|
51
|
+
let lo = max(rowPtr[v], P.arcBase);
|
|
52
|
+
let hi = min(rowPtr[v + 1u], P.arcEnd);
|
|
53
|
+
var acc = 0.0;
|
|
54
|
+
var chunk = 0.0;
|
|
55
|
+
var inChunk = 0u;
|
|
56
|
+
for (var arc = lo; arc < hi; arc = arc + 1u) {
|
|
57
|
+
let k = arc - P.arcBase; // the window-local index; this walk is contiguous
|
|
58
|
+
let nbr = colIdx[k]; // \`target\` is a WGSL reserved word (spec 16.2)
|
|
59
|
+
var weight = 1.0;
|
|
60
|
+
if (HAS_WEIGHTS) { weight = weights[k]; }
|
|
61
|
+
// two-level sum: 64 terms into chunk, chunk into acc (see the header; no compensation, no select)
|
|
62
|
+
chunk = chunk + (weight * xNorm[nbr]);
|
|
63
|
+
inChunk = inChunk + 1u;
|
|
64
|
+
if (inChunk == 64u) {
|
|
65
|
+
acc = acc + chunk;
|
|
66
|
+
chunk = 0.0;
|
|
67
|
+
inChunk = 0u;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
acc = acc + chunk;
|
|
71
|
+
return acc;
|
|
72
|
+
}
|
|
73
|
+
fn finish(v: u32, acc: f32) {
|
|
18
74
|
var dangling = 0.0;
|
|
19
75
|
if (USE_DANGLING) { dangling = partials[0].danglingMass; }
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
76
|
+
var pv = P.uniformP;
|
|
77
|
+
if (HAS_PERSONALIZATION) { pv = personalization[v]; }
|
|
78
|
+
rankOut[v] = (P.beta * pv) + (P.alpha * (acc + (dangling * pv)));
|
|
79
|
+
}
|
|
80
|
+
fn tier0(wid: vec3<u32>, lane: u32) { // TIER 0: grid-stride over the rows [P.start, P.n); no barrier
|
|
81
|
+
for (var row = linear_id(wid, lane) + P.start; row < P.n; row = row + P.stride) {
|
|
82
|
+
let v = row_node(row);
|
|
83
|
+
finish(v, row_sum_dense(v));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
var<workgroup> sh: array<f32, WG>;
|
|
88
|
+
|
|
89
|
+
fn tiered(wid: vec3<u32>, lid: u32) { // TIER 1: 32 lanes per row; TIER 2: WG lanes per row; rows [P.start, P.n) (PD-6)
|
|
90
|
+
let g = group_id(wid);
|
|
91
|
+
var row = P.start + g;
|
|
92
|
+
var lane = lid;
|
|
93
|
+
var step = WG;
|
|
94
|
+
if (TIER == 1u) { row = P.start + g * (WG / 32u) + lid / 32u; lane = lid % 32u; step = 32u; }
|
|
95
|
+
let valid = row < P.n;
|
|
96
|
+
var v = 0u;
|
|
97
|
+
var acc = 0.0;
|
|
98
|
+
if (valid) { v = row_node(row); acc = row_sum(v, lane, step); }
|
|
99
|
+
if (TIER == 1u) {
|
|
100
|
+
sh[lid] = acc;
|
|
101
|
+
workgroupBarrier();
|
|
102
|
+
for (var s = 16u; s >= 1u; s = s / 2u) { // the five-step tree over each 32-lane group; every lane runs every step
|
|
103
|
+
var t = 0.0;
|
|
104
|
+
if (lane < s) { t = sh[lid + s]; }
|
|
105
|
+
workgroupBarrier();
|
|
106
|
+
sh[lid] = sh[lid] + t;
|
|
107
|
+
workgroupBarrier();
|
|
40
108
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
109
|
+
if (valid && lane == 0u) { finish(v, sh[lid]); }
|
|
110
|
+
}
|
|
111
|
+
if (TIER == 2u) {
|
|
112
|
+
let t = wg_reduce_f32(acc, lid, 0u);
|
|
113
|
+
if (valid && lid == 0u) { finish(v, t); }
|
|
45
114
|
}
|
|
46
115
|
}
|
|
116
|
+
|
|
117
|
+
@compute @workgroup_size(WG)
|
|
118
|
+
fn spmv_pull(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
119
|
+
if (TIER == 0u) { tier0(wid, lid.x); } else { tiered(wid, lid.x); }
|
|
120
|
+
}
|
|
47
121
|
`;
|