@graphty/webgpu-graph-algorithms 0.2.1 → 0.3.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 +45 -36
- package/dist/browser.js +1 -1
- package/dist/chunks/{context-E6iKaeuJ.js → context-CRbw2Wyo.js} +178 -19
- package/dist/chunks/{context-E6iKaeuJ.js.map → context-CRbw2Wyo.js.map} +1 -1
- package/dist/node.js +1 -1
- package/dist/src/accelerator.d.ts +9 -6
- package/dist/src/accelerator.d.ts.map +1 -1
- package/dist/src/accelerator.js +85 -6
- package/dist/src/accelerator.js.map +1 -1
- package/dist/src/algorithms/components.d.ts +30 -0
- package/dist/src/algorithms/components.d.ts.map +1 -0
- package/dist/src/algorithms/components.js +300 -0
- package/dist/src/algorithms/components.js.map +1 -0
- package/dist/src/algorithms/pagerank.d.ts +39 -0
- package/dist/src/algorithms/pagerank.d.ts.map +1 -0
- package/dist/src/algorithms/pagerank.js +298 -0
- package/dist/src/algorithms/pagerank.js.map +1 -0
- package/dist/src/algorithms/power-iteration.d.ts +109 -0
- package/dist/src/algorithms/power-iteration.d.ts.map +1 -0
- package/dist/src/algorithms/power-iteration.js +206 -0
- package/dist/src/algorithms/power-iteration.js.map +1 -0
- package/dist/src/algorithms/scope.d.ts +26 -0
- package/dist/src/algorithms/scope.d.ts.map +1 -0
- package/dist/src/algorithms/scope.js +41 -0
- package/dist/src/algorithms/scope.js.map +1 -0
- package/dist/src/algorithms/spectral.d.ts +50 -0
- package/dist/src/algorithms/spectral.d.ts.map +1 -0
- package/dist/src/algorithms/spectral.js +247 -0
- package/dist/src/algorithms/spectral.js.map +1 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/kernel/dispatch.d.ts +4 -1
- package/dist/src/kernel/dispatch.d.ts.map +1 -1
- package/dist/src/kernel/dispatch.js +12 -5
- package/dist/src/kernel/dispatch.js.map +1 -1
- package/dist/src/kernels.d.ts +20 -4
- package/dist/src/kernels.d.ts.map +1 -1
- package/dist/src/kernels.js +172 -2
- package/dist/src/kernels.js.map +1 -1
- package/dist/src/memory/residency.d.ts.map +1 -1
- package/dist/src/memory/residency.js +164 -11
- package/dist/src/memory/residency.js.map +1 -1
- package/dist/src/primitives/core-shape.d.ts +41 -0
- package/dist/src/primitives/core-shape.d.ts.map +1 -0
- package/dist/src/primitives/core-shape.js +89 -0
- package/dist/src/primitives/core-shape.js.map +1 -0
- package/dist/src/primitives/segmented-reduce.d.ts.map +1 -1
- package/dist/src/primitives/segmented-reduce.js +4 -30
- package/dist/src/primitives/segmented-reduce.js.map +1 -1
- package/dist/src/primitives/spmv.d.ts +56 -0
- package/dist/src/primitives/spmv.d.ts.map +1 -0
- package/dist/src/primitives/spmv.js +101 -0
- package/dist/src/primitives/spmv.js.map +1 -0
- package/dist/src/types/accelerator.d.ts +14 -2
- package/dist/src/types/accelerator.d.ts.map +1 -1
- package/dist/src/types/algorithms.d.ts +73 -0
- package/dist/src/types/algorithms.d.ts.map +1 -0
- package/dist/src/types/algorithms.js +17 -0
- package/dist/src/types/algorithms.js.map +1 -0
- package/dist/src/wgsl/pr-finalize.wgsl.d.ts +11 -0
- package/dist/src/wgsl/pr-finalize.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/pr-finalize.wgsl.js +36 -0
- package/dist/src/wgsl/pr-finalize.wgsl.js.map +1 -0
- package/dist/src/wgsl/pr-scale.wgsl.d.ts +14 -0
- package/dist/src/wgsl/pr-scale.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/pr-scale.wgsl.js +48 -0
- package/dist/src/wgsl/pr-scale.wgsl.js.map +1 -0
- package/dist/src/wgsl/spmv-pull.wgsl.d.ts +15 -0
- package/dist/src/wgsl/spmv-pull.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/spmv-pull.wgsl.js +47 -0
- package/dist/src/wgsl/spmv-pull.wgsl.js.map +1 -0
- package/dist/src/wgsl/wcc-compress.wgsl.d.ts +9 -0
- package/dist/src/wgsl/wcc-compress.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/wcc-compress.wgsl.js +26 -0
- package/dist/src/wgsl/wcc-compress.wgsl.js.map +1 -0
- package/dist/src/wgsl/wcc-link-edges.wgsl.d.ts +13 -0
- package/dist/src/wgsl/wcc-link-edges.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/wcc-link-edges.wgsl.js +46 -0
- package/dist/src/wgsl/wcc-link-edges.wgsl.js.map +1 -0
- package/dist/src/wgsl/wcc-link-sample.wgsl.d.ts +11 -0
- package/dist/src/wgsl/wcc-link-sample.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/wcc-link-sample.wgsl.js +45 -0
- package/dist/src/wgsl/wcc-link-sample.wgsl.js.map +1 -0
- package/dist/src/wgsl/wcc-sample.wgsl.d.ts +10 -0
- package/dist/src/wgsl/wcc-sample.wgsl.d.ts.map +1 -0
- package/dist/src/wgsl/wcc-sample.wgsl.js +18 -0
- package/dist/src/wgsl/wcc-sample.wgsl.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/webgpu-graph-algorithms.js +1550 -29
- package/dist/webgpu-graph-algorithms.js.map +1 -1
- package/package.json +1 -1
- package/src/accelerator.ts +101 -7
- package/src/algorithms/components.ts +348 -0
- package/src/algorithms/pagerank.ts +343 -0
- package/src/algorithms/power-iteration.ts +278 -0
- package/src/algorithms/scope.ts +52 -0
- package/src/algorithms/spectral.ts +300 -0
- package/src/index.ts +18 -0
- package/src/kernel/dispatch.ts +12 -5
- package/src/kernels.ts +206 -5
- package/src/memory/residency.ts +200 -11
- package/src/primitives/core-shape.ts +103 -0
- package/src/primitives/segmented-reduce.ts +4 -36
- package/src/primitives/spmv.ts +155 -0
- package/src/types/accelerator.ts +28 -2
- package/src/types/algorithms.ts +82 -0
- package/src/wgsl/pr-finalize.wgsl.ts +36 -0
- package/src/wgsl/pr-scale.wgsl.ts +48 -0
- package/src/wgsl/spmv-pull.wgsl.ts +47 -0
- package/src/wgsl/wcc-compress.wgsl.ts +26 -0
- package/src/wgsl/wcc-link-edges.wgsl.ts +46 -0
- package/src/wgsl/wcc-link-sample.wgsl.ts +45 -0
- package/src/wgsl/wcc-sample.wgsl.ts +18 -0
package/README.md
CHANGED
|
@@ -287,32 +287,39 @@ are the T-table of plan section 10.4. A missed target is re-fixed by a recorded
|
|
|
287
287
|
|
|
288
288
|
### The dev box (nvidia-lovelace-driver580)
|
|
289
289
|
|
|
290
|
-
Measured on nvidia-lovelace-driver580 (NVIDIA: 580.173.02 580.173.2.0), session 2026-09-
|
|
291
|
-
|
|
292
|
-
| Id | What
|
|
293
|
-
| --- |
|
|
294
|
-
| T-1 | Upload of the 100k / 1M weighted hot prefix (16.4 MB); 1M / 10M (164 MB)
|
|
295
|
-
| T-2 | `degree` + 400 KB readback at 100k (core resident), Node
|
|
296
|
-
| T-3 | Empty submit + 4-byte `readU32` round trip, Dawn
|
|
297
|
-
| T-4 | ForceAtlas2 exact tier, GPU time per iteration (profiler) at 10k; at 16k
|
|
298
|
-
| T-5 | ForceAtlas2 per-frame cost, `step(1)` + the 12n readback at 10k, Chromium (Node in brackets)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
290
|
+
Measured on nvidia-lovelace-driver580 (NVIDIA: 580.173.02 580.173.2.0), session 2026-09-20T01:06:48.656Z, medians of 5 runs; Chromium: nvidia / lovelace (nvidia-lovelace-driver0, the description is redacted by Chromium), session 2026-09-16T02:18:11.896Z.
|
|
291
|
+
|
|
292
|
+
| Id | What | Target | Measured |
|
|
293
|
+
| --- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ---------------------- |
|
|
294
|
+
| T-1 | Upload of the 100k / 1M weighted hot prefix (16.4 MB); 1M / 10M (164 MB) | <= 10 ms; <= 100 ms | 5.980 ms; 125.738 ms |
|
|
295
|
+
| T-2 | `degree` + 400 KB readback at 100k (core resident), Node | <= 2 ms | 0.870 ms |
|
|
296
|
+
| T-3 | Empty submit + 4-byte `readU32` round trip, Dawn | <= 0.1 ms | 0.181 ms |
|
|
297
|
+
| T-4 | ForceAtlas2 exact tier, GPU time per iteration (profiler) at 10k; at 16k | <= 1 ms; <= 2 ms | 0.586 ms; 1.053 ms |
|
|
298
|
+
| T-5 | ForceAtlas2 per-frame cost, `step(1)` + the 12n readback at 10k, Chromium (Node in brackets) | <= 6 ms | 2.400 ms (0.726 ms) |
|
|
299
|
+
| T-8 | PageRank, 100 iterations, wall end to end including the upload, at 100k / 1M; at 1M / 10M | <= 150 ms; <= 1.5 s | 17.204 ms; 198.645 ms |
|
|
300
|
+
| T-9 | Weakly connected components (Afforest), wall end to end including the upload and the label readback, at 1M / 10M (100k / 1M in brackets) | <= 100 ms | 145.970 ms (12.613 ms) |
|
|
301
|
+
|
|
302
|
+
Three rows miss their target in this session: the 1M / 10M upload (125.7 ms against 100 ms, the open owner decision of
|
|
303
|
+
`docs/decisions/G1.md` section 7), the empty-submit round trip (0.181 ms against 0.1 ms: the row is measured after the
|
|
304
|
+
`upload` group, whose CPU-heavy setup lets the SM clock fall to its idle state; the same row measures 0.041-0.075 ms at
|
|
305
|
+
the working clock -- finding G3-F2 of `docs/decisions/G3.md` section 10), and WCC at 1M / 10M (146.0 ms against
|
|
306
|
+
100 ms: the row is wall end to end from a released core, so it carries the same 164 MB upload T-1 times at 125.7 ms;
|
|
307
|
+
with the core resident the same call takes 11-16 ms). The T-9 target is therefore below
|
|
308
|
+
the T-1 upload it includes, an owner decision for `docs/decisions/G7.md`. The `pagerank` rows run all 100 iterations
|
|
309
|
+
(`tolerance: 0`): at the NetworkX tolerance of 1e-6 the seeded G(n, m) input converges from the uniform start in one to
|
|
310
|
+
four iterations, which would time one pull and call it a hundred.
|
|
304
311
|
|
|
305
312
|
The exact curve (the `layout-exact` group: 2D, E = 10n, seeded G(n, m), one simulation per rung; ms / iteration from the profiler):
|
|
306
313
|
|
|
307
314
|
| n | ms / iteration | step(1) wall (ms) | pairs / s |
|
|
308
315
|
| ----- | -------------- | ----------------- | --------- |
|
|
309
|
-
| 1024 | 0.
|
|
310
|
-
| 4096 | 0.255 | 0.
|
|
311
|
-
| 8192 | 0.478 | 0.
|
|
312
|
-
| 10000 | 0.586 | 0.
|
|
313
|
-
| 16384 | 1.
|
|
314
|
-
| 32768 | 2.
|
|
315
|
-
| 65536 | 8.
|
|
316
|
+
| 1024 | 0.087 | 0.224 | 1.20e+10 |
|
|
317
|
+
| 4096 | 0.255 | 0.381 | 6.58e+10 |
|
|
318
|
+
| 8192 | 0.478 | 0.612 | 1.40e+11 |
|
|
319
|
+
| 10000 | 0.586 | 0.726 | 1.71e+11 |
|
|
320
|
+
| 16384 | 1.053 | 1.207 | 2.55e+11 |
|
|
321
|
+
| 32768 | 2.561 | 2.757 | 4.19e+11 |
|
|
322
|
+
| 65536 | 8.402 | 8.705 | 5.11e+11 |
|
|
316
323
|
|
|
317
324
|
The end-to-end run of `benchmarks/layout-run.ts --nodes 100000 --edges 1000000` (the exact tier at 100k, 100
|
|
318
325
|
iterations, batches of 8) takes 18.971 ms per iteration on the same card, uploads and readbacks included (16.975 ms of
|
|
@@ -327,27 +334,29 @@ T-4 and T-5 and misses T-1 (both uploads), T-2 and T-3, which is the class diffe
|
|
|
327
334
|
cloud vCPU (host-side copies and submit latency), not a regression: the exact tier's `ms / iteration` is 1.7x the
|
|
328
335
|
RTX 4070 SUPER's at 10k and 3.0x at 65k.
|
|
329
336
|
|
|
330
|
-
Measured on gpu-linux-t4 (NVIDIA: 580.126.20 580.126.20.0), session 2026-09-
|
|
337
|
+
Measured on gpu-linux-t4 (NVIDIA: 580.126.20 580.126.20.0), session 2026-09-20T02:29:33.210Z (run 35483512705), medians of 5 runs; Chromium: nvidia / turing (nvidia-turing-driver0, the description is redacted by Chromium), session 2026-09-20T02:28:10.676Z.
|
|
331
338
|
|
|
332
|
-
| Id | What
|
|
333
|
-
| --- |
|
|
334
|
-
| T-1 | Upload of the 100k / 1M weighted hot prefix (16.4 MB); 1M / 10M (164 MB)
|
|
335
|
-
| T-2 | `degree` + 400 KB readback at 100k (core resident), Node
|
|
336
|
-
| T-3 | Empty submit + 4-byte `readU32` round trip, Dawn
|
|
337
|
-
| T-4 | ForceAtlas2 exact tier, GPU time per iteration (profiler) at 10k; at 16k
|
|
338
|
-
| T-5 | ForceAtlas2 per-frame cost, `step(1)` + the 12n readback at 10k, Chromium (Node in brackets)
|
|
339
|
+
| Id | What | Target | Measured |
|
|
340
|
+
| --- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ---------------------- |
|
|
341
|
+
| T-1 | Upload of the 100k / 1M weighted hot prefix (16.4 MB); 1M / 10M (164 MB) | <= 10 ms; <= 100 ms | 14.338 ms; 256.418 ms |
|
|
342
|
+
| T-2 | `degree` + 400 KB readback at 100k (core resident), Node | <= 2 ms | 2.957 ms |
|
|
343
|
+
| T-3 | Empty submit + 4-byte `readU32` round trip, Dawn | <= 0.1 ms | 1.296 ms |
|
|
344
|
+
| T-4 | ForceAtlas2 exact tier, GPU time per iteration (profiler) at 10k; at 16k | <= 1 ms; <= 2 ms | 0.972 ms; 1.953 ms |
|
|
345
|
+
| T-5 | ForceAtlas2 per-frame cost, `step(1)` + the 12n readback at 10k, Chromium (Node in brackets) | <= 6 ms | 2.600 ms (1.359 ms) |
|
|
346
|
+
| T-8 | PageRank, 100 iterations, wall end to end including the upload, at 100k / 1M; at 1M / 10M | <= 150 ms; <= 1.5 s | 45.461 ms; 1092.799 ms |
|
|
347
|
+
| T-9 | Weakly connected components (Afforest), wall end to end including the upload and the label readback, at 1M / 10M (100k / 1M in brackets) | <= 100 ms | 293.079 ms (28.544 ms) |
|
|
339
348
|
|
|
340
349
|
The exact curve (the `layout-exact` group: 2D, E = 10n, seeded G(n, m), one simulation per rung; ms / iteration from the profiler):
|
|
341
350
|
|
|
342
351
|
| n | ms / iteration | step(1) wall (ms) | pairs / s |
|
|
343
352
|
| ----- | -------------- | ----------------- | --------- |
|
|
344
|
-
| 1024 | 0.
|
|
345
|
-
| 4096 | 0.
|
|
346
|
-
| 8192 | 0.
|
|
347
|
-
| 10000 | 0.
|
|
348
|
-
| 16384 | 1.
|
|
349
|
-
| 32768 | 6.
|
|
350
|
-
| 65536 |
|
|
353
|
+
| 1024 | 0.340 | 0.711 | 3.08e+9 |
|
|
354
|
+
| 4096 | 0.431 | 0.814 | 3.90e+10 |
|
|
355
|
+
| 8192 | 0.797 | 1.172 | 8.42e+10 |
|
|
356
|
+
| 10000 | 0.972 | 1.359 | 1.03e+11 |
|
|
357
|
+
| 16384 | 1.953 | 2.383 | 1.37e+11 |
|
|
358
|
+
| 32768 | 6.861 | 7.394 | 1.57e+11 |
|
|
359
|
+
| 65536 | 25.236 | 26.366 | 1.70e+11 |
|
|
351
360
|
|
|
352
361
|
## Development
|
|
353
362
|
|
package/dist/browser.js
CHANGED
|
@@ -2457,22 +2457,38 @@ class GraphResidency {
|
|
|
2457
2457
|
});
|
|
2458
2458
|
}
|
|
2459
2459
|
/**
|
|
2460
|
-
* Uploads (or finds) a view
|
|
2461
|
-
*
|
|
2462
|
-
*
|
|
2460
|
+
* Uploads (or finds) a view: outDegree / inDegree / degreeOrder / reverseDegreeOrder upload one array each;
|
|
2461
|
+
* reverse and edgeList (P7) upload their arrays perArray, never into the arena, and are memoised per record so
|
|
2462
|
+
* a second call uploads nothing (spec 4.3). coo and mate -> E_UNSUPPORTED until P11. packViews concatenates a
|
|
2463
|
+
* reverse or edgeList view into ONE buffer at STORAGE_ALIGN offsets; on any other view `true` is E_UNSUPPORTED
|
|
2464
|
+
* { option: "packViews" }.
|
|
2463
2465
|
* @param s - the snapshot
|
|
2464
2466
|
* @param name - the view
|
|
2465
2467
|
* @param options - view options
|
|
2466
|
-
* @param options.packViews -
|
|
2468
|
+
* @param options.packViews - pack the reverse / edgeList arrays into one buffer (a no-op on an undirected reverse)
|
|
2467
2469
|
* @returns the view binding
|
|
2468
2470
|
*/
|
|
2469
2471
|
view(s, name, options) {
|
|
2470
2472
|
this.assertLive();
|
|
2471
2473
|
assertAttached(s);
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
+
const packed = options?.packViews === true;
|
|
2475
|
+
if (name === "reverse" || name === "edgeList") {
|
|
2476
|
+
this.assertNotReleased(s);
|
|
2477
|
+
this.assertNonEmpty(s);
|
|
2478
|
+
const memoKey = packed ? `${name}:packed` : name;
|
|
2479
|
+
const record2 = this.ensureRecord(s);
|
|
2480
|
+
const memo = record2.views.get(memoKey);
|
|
2481
|
+
if (memo !== void 0) {
|
|
2482
|
+
return memo;
|
|
2483
|
+
}
|
|
2484
|
+
const built = name === "reverse" ? this.buildReverse(s, record2, packed) : this.buildEdgeList(s, record2, packed);
|
|
2485
|
+
record2.views.set(memoKey, built);
|
|
2486
|
+
return built;
|
|
2487
|
+
}
|
|
2488
|
+
if (packed) {
|
|
2489
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", "packViews applies to the reverse and edgeList views only", {
|
|
2474
2490
|
option: "packViews",
|
|
2475
|
-
hint:
|
|
2491
|
+
hint: `the ${name} view uploads one array`
|
|
2476
2492
|
});
|
|
2477
2493
|
}
|
|
2478
2494
|
let array;
|
|
@@ -2509,13 +2525,11 @@ class GraphResidency {
|
|
|
2509
2525
|
scalars = { segmentOffsets: Array.from(order.segmentOffsets) };
|
|
2510
2526
|
break;
|
|
2511
2527
|
}
|
|
2512
|
-
case "reverse":
|
|
2513
2528
|
case "coo":
|
|
2514
|
-
case "edgeList":
|
|
2515
2529
|
case "mate":
|
|
2516
|
-
throw new WebGpuGraphError("E_UNSUPPORTED", `the ${name} view is not uploaded before
|
|
2530
|
+
throw new WebGpuGraphError("E_UNSUPPORTED", `the ${name} view is not uploaded before P11`, {
|
|
2517
2531
|
feature: `view:${name}`,
|
|
2518
|
-
hint: "
|
|
2532
|
+
hint: "outDegree, inDegree, degreeOrder, reverseDegreeOrder, reverse and edgeList are uploaded"
|
|
2519
2533
|
});
|
|
2520
2534
|
default:
|
|
2521
2535
|
throw invalid("name", name, "a view name");
|
|
@@ -2529,6 +2543,146 @@ class GraphResidency {
|
|
|
2529
2543
|
scalars: Object.freeze(scalars)
|
|
2530
2544
|
});
|
|
2531
2545
|
}
|
|
2546
|
+
/**
|
|
2547
|
+
* The reverse adjacency's bindings. On an UNDIRECTED snapshot graph-format invariant I7 makes reverse() return
|
|
2548
|
+
* the FORWARD arrays, so the core bindings ARE the reverse bindings and nothing is uploaded -- delegating to
|
|
2549
|
+
* core() rather than re-uploading the arrays is what makes that true on the arena plan as well, where the
|
|
2550
|
+
* per-array WeakMap key resolves to the whole-arena resident (spec 4.3 lines 1182-1186). `fwdArc` is never
|
|
2551
|
+
* uploaded (spec 13 row P7). `packed` is ignored when undirected: there is nothing to pack.
|
|
2552
|
+
* @param s - the snapshot
|
|
2553
|
+
* @param record - its residency record
|
|
2554
|
+
* @param packed - concatenate the arrays into one buffer (directed only)
|
|
2555
|
+
* @returns the view binding
|
|
2556
|
+
*/
|
|
2557
|
+
buildReverse(s, record, packed) {
|
|
2558
|
+
const rev = s.reverse();
|
|
2559
|
+
if (!s.directed) {
|
|
2560
|
+
const core = this.core(s, ["rowPtr", "colIdx", "weights"]);
|
|
2561
|
+
const bindings2 = { rowPtr: core.rowPtr };
|
|
2562
|
+
if (core.colIdx !== null) {
|
|
2563
|
+
bindings2.colIdx = core.colIdx;
|
|
2564
|
+
}
|
|
2565
|
+
if (core.weights !== null) {
|
|
2566
|
+
bindings2.weights = core.weights;
|
|
2567
|
+
}
|
|
2568
|
+
return Object.freeze({
|
|
2569
|
+
view: "reverse",
|
|
2570
|
+
bindings: Object.freeze(bindings2),
|
|
2571
|
+
scalars: Object.freeze({ arcCount: [rev.arcCount], directed: [0] })
|
|
2572
|
+
});
|
|
2573
|
+
}
|
|
2574
|
+
const arrays = [
|
|
2575
|
+
["rowPtr", rev.rowPtr],
|
|
2576
|
+
["colIdx", rev.colIdx]
|
|
2577
|
+
];
|
|
2578
|
+
if (rev.weights !== null) {
|
|
2579
|
+
arrays.push(["weights", rev.weights]);
|
|
2580
|
+
}
|
|
2581
|
+
const bindings = packed ? this.packArrays(
|
|
2582
|
+
record,
|
|
2583
|
+
arrays,
|
|
2584
|
+
this.packKey(record, "reverse:packed"),
|
|
2585
|
+
`residency:view:${record.serial}:reverse:packed`
|
|
2586
|
+
) : this.separateArrays(record, arrays, `residency:view:${record.serial}:reverse`);
|
|
2587
|
+
return Object.freeze({
|
|
2588
|
+
view: "reverse",
|
|
2589
|
+
bindings,
|
|
2590
|
+
scalars: Object.freeze({ arcCount: [rev.arcCount], directed: [1] })
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2593
|
+
/**
|
|
2594
|
+
* The each-edge-once binding an edge-parallel kernel uses on directed and undirected snapshots alike (spec 8.1
|
|
2595
|
+
* row 2); `arc` is not uploaded (no kernel of P7 reads it).
|
|
2596
|
+
* @param s - the snapshot
|
|
2597
|
+
* @param record - its residency record
|
|
2598
|
+
* @param packed - concatenate into one buffer
|
|
2599
|
+
* @returns the view binding
|
|
2600
|
+
*/
|
|
2601
|
+
buildEdgeList(s, record, packed) {
|
|
2602
|
+
const list = s.edgeList();
|
|
2603
|
+
const arrays = [
|
|
2604
|
+
["src", list.src],
|
|
2605
|
+
["dst", list.dst]
|
|
2606
|
+
];
|
|
2607
|
+
if (list.weights !== null) {
|
|
2608
|
+
arrays.push(["weights", list.weights]);
|
|
2609
|
+
}
|
|
2610
|
+
const bindings = packed ? this.packArrays(
|
|
2611
|
+
record,
|
|
2612
|
+
arrays,
|
|
2613
|
+
this.packKey(record, "edgeList:packed"),
|
|
2614
|
+
`residency:view:${record.serial}:edgeList:packed`
|
|
2615
|
+
) : this.separateArrays(record, arrays, `residency:view:${record.serial}:edgeList`);
|
|
2616
|
+
return Object.freeze({
|
|
2617
|
+
view: "edgeList",
|
|
2618
|
+
bindings,
|
|
2619
|
+
scalars: Object.freeze({ edgeCount: [s.edgeCount] })
|
|
2620
|
+
});
|
|
2621
|
+
}
|
|
2622
|
+
/**
|
|
2623
|
+
* One resident per array (spec 4.3: views upload in perArray mode, never into the arena).
|
|
2624
|
+
* @param record - the owning record
|
|
2625
|
+
* @param arrays - the named arrays
|
|
2626
|
+
* @param label - the buffer label prefix
|
|
2627
|
+
* @returns the bindings by name
|
|
2628
|
+
*/
|
|
2629
|
+
separateArrays(record, arrays, label) {
|
|
2630
|
+
const bindings = {};
|
|
2631
|
+
for (const [name, array] of arrays) {
|
|
2632
|
+
const resident = this.upload(record, array, array, `${label}:${name}`);
|
|
2633
|
+
bindings[name] = { buffer: resident.buffer, offset: 0, size: resident.byteLength, window: null };
|
|
2634
|
+
}
|
|
2635
|
+
return Object.freeze(bindings);
|
|
2636
|
+
}
|
|
2637
|
+
/**
|
|
2638
|
+
* The arrays concatenated into ONE buffer at STORAGE_ALIGN-aligned offsets (spec 4.3 packViews): one
|
|
2639
|
+
* createBuffer and one writeBuffer instead of three of each, which is what the option buys. The resident is
|
|
2640
|
+
* keyed on `key`, a marker object owned by the record (packKey), NOT on one of the snapshot's arrays: upload()
|
|
2641
|
+
* returns an existing resident whenever the key matches and the serial matches, without comparing byte
|
|
2642
|
+
* lengths, so keying the packed buffer on `rev.rowPtr` would make the packed and the unpacked view of one
|
|
2643
|
+
* snapshot collide -- whichever was built second would get the other's buffer. The record still owns the
|
|
2644
|
+
* resident, so release(s) destroys it with the rest. Offsets are STORAGE_ALIGN-aligned because Kernel.bind
|
|
2645
|
+
* rejects any other offset synchronously (E_INVALID_ARGUMENT { argument: "offset" }).
|
|
2646
|
+
* @param record - the owning record
|
|
2647
|
+
* @param arrays - the named arrays, in buffer order
|
|
2648
|
+
* @param key - the marker object the resident is keyed on
|
|
2649
|
+
* @param label - the buffer label
|
|
2650
|
+
* @returns the bindings by name, all into the one buffer
|
|
2651
|
+
*/
|
|
2652
|
+
packArrays(record, arrays, key, label) {
|
|
2653
|
+
const offsets = [];
|
|
2654
|
+
let total = 0;
|
|
2655
|
+
for (const [, array] of arrays) {
|
|
2656
|
+
offsets.push(total);
|
|
2657
|
+
total += Math.ceil(array.byteLength / STORAGE_ALIGN) * STORAGE_ALIGN;
|
|
2658
|
+
}
|
|
2659
|
+
const staging = new Uint8Array(total);
|
|
2660
|
+
arrays.forEach(([, array], i) => {
|
|
2661
|
+
staging.set(new Uint8Array(array.buffer, array.byteOffset, array.byteLength), offsets[i]);
|
|
2662
|
+
});
|
|
2663
|
+
const resident = this.upload(record, key, staging, label);
|
|
2664
|
+
const bindings = {};
|
|
2665
|
+
arrays.forEach(([name, array], i) => {
|
|
2666
|
+
bindings[name] = { buffer: resident.buffer, offset: offsets[i], size: array.byteLength, window: null };
|
|
2667
|
+
});
|
|
2668
|
+
return Object.freeze(bindings);
|
|
2669
|
+
}
|
|
2670
|
+
/**
|
|
2671
|
+
* The upload key of a packed view: a marker object allocated once per record and memo name, never one of the
|
|
2672
|
+
* snapshot's arrays.
|
|
2673
|
+
* @param record - the owning record
|
|
2674
|
+
* @param memoKey - the `views` memo key of this packed view
|
|
2675
|
+
* @returns the stable marker object
|
|
2676
|
+
*/
|
|
2677
|
+
packKey(record, memoKey) {
|
|
2678
|
+
const existing = record.packKeys.get(memoKey);
|
|
2679
|
+
if (existing !== void 0) {
|
|
2680
|
+
return existing;
|
|
2681
|
+
}
|
|
2682
|
+
const created = {};
|
|
2683
|
+
record.packKeys.set(memoKey, created);
|
|
2684
|
+
return created;
|
|
2685
|
+
}
|
|
2532
2686
|
/**
|
|
2533
2687
|
* gpuView(name) + column.version; re-uploads in place when the version changed and the byte length did not
|
|
2534
2688
|
* (spec 4.3). CONTRACT DECISION: `owner` is required so release(owner) can find the buffer (a table has no
|
|
@@ -2764,6 +2918,8 @@ class GraphResidency {
|
|
|
2764
2918
|
forget(record) {
|
|
2765
2919
|
record.released = true;
|
|
2766
2920
|
record.bindings.clear();
|
|
2921
|
+
record.views.clear();
|
|
2922
|
+
record.packKeys.clear();
|
|
2767
2923
|
record.arena = null;
|
|
2768
2924
|
record.plan = null;
|
|
2769
2925
|
this.bySerial.delete(record.serial);
|
|
@@ -2789,6 +2945,8 @@ class GraphResidency {
|
|
|
2789
2945
|
plan: null,
|
|
2790
2946
|
arena: null,
|
|
2791
2947
|
bindings: /* @__PURE__ */ new Map(),
|
|
2948
|
+
views: /* @__PURE__ */ new Map(),
|
|
2949
|
+
packKeys: /* @__PURE__ */ new Map(),
|
|
2792
2950
|
released: false
|
|
2793
2951
|
};
|
|
2794
2952
|
this.bySerial.set(s.serial, record);
|
|
@@ -3122,15 +3280,16 @@ export {
|
|
|
3122
3280
|
UNIFORM_SLOT_BYTES as U,
|
|
3123
3281
|
WebGpuGraphError as W,
|
|
3124
3282
|
WGSL_RESERVED_WORDS as a,
|
|
3125
|
-
|
|
3126
|
-
|
|
3283
|
+
U32_MAX as b,
|
|
3284
|
+
MAX_ITERATIONS_PER_STEP as c,
|
|
3127
3285
|
deviceLostError as d,
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3286
|
+
MAX_1D_ITEMS as e,
|
|
3287
|
+
FA2_FLAG_FIRST as f,
|
|
3288
|
+
PASSTHROUGH_FORMAT_CODES as g,
|
|
3131
3289
|
hasErrorCode as h,
|
|
3132
3290
|
isWebGpuGraphError as i,
|
|
3133
|
-
|
|
3134
|
-
|
|
3291
|
+
STORAGE_ALIGN as j,
|
|
3292
|
+
WORKGROUP_SIZE as k,
|
|
3293
|
+
isSoftwareAdapter as l
|
|
3135
3294
|
};
|
|
3136
|
-
//# sourceMappingURL=context-
|
|
3295
|
+
//# sourceMappingURL=context-CRbw2Wyo.js.map
|