@danielsimonjr/mathts-functions 0.18.0 → 0.19.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.
|
@@ -7,11 +7,18 @@
|
|
|
7
7
|
* storage buffers, and reads back once, so the transfer is amortized across the
|
|
8
8
|
* whole chain.
|
|
9
9
|
*
|
|
10
|
-
* **Read this before reaching for the GPU:** for element-wise
|
|
11
|
-
* *
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* **Read this before reaching for the GPU:** for element-wise chains the GPU is
|
|
11
|
+
* the *fastest* tier (3.2–8.3× over WASM — see the table on
|
|
12
|
+
* `fuseUnaryChainAsync`), but it computes in **f32** where every CPU tier is
|
|
13
|
+
* f64-exact. That is the whole trade, and `enableGpu()` is how a caller consents
|
|
14
|
+
* to it. `fuseUnaryChainAsync` therefore tries the GPU first, but only when the
|
|
15
|
+
* flag is on; with it off (the default) the GPU never runs.
|
|
16
|
+
*
|
|
17
|
+
* An earlier revision of this comment claimed the GPU was ~1.9× *slower* than
|
|
18
|
+
* WASM. That was an artifact of a `Float32Array.from()` in this very file — the
|
|
19
|
+
* generic `Array.from` path, which cost 433 ms at n=2²⁰ where the constructor
|
|
20
|
+
* costs 5.9 ms. Do not re-derive a tier ranking from a single tier's number; see
|
|
21
|
+
* `gpu-vs-wasm.browser.test.ts`, which measures all three in one run.
|
|
15
22
|
*
|
|
16
23
|
* Contract (mirrors the WASM `elementwiseChainDispatch`): a **never-throw**
|
|
17
24
|
* best-effort fast path. It returns `null` — never rejects — whenever the GPU is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"elementwise-gpu.d.ts","sourceRoot":"","sources":["../../src/gpu/elementwise-gpu.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"elementwise-gpu.d.ts","sourceRoot":"","sources":["../../src/gpu/elementwise-gpu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAOL,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AA+CnC,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;CAgCR,CAAC;AAEX,uEAAuE;AACvE,MAAM,MAAM,gBAAgB,GAAG,MAAM,OAAO,YAAY,CAAC;AAEzD,eAAO,MAAM,mBAAmB,EAAgC,gBAAgB,EAAE,CAAC;AAEnF,sDAAsD;AACtD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,GAAG,IAAI,SAAS,gBAAgB,EAAE,CAE9F;AAsED,uEAAuE;AACvE,wBAAgB,mBAAmB,IAAI,IAAI,CAG1C;AAED,+CAA+C;AAC/C,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,SAAS,MAAM,EAAE,EACtB,EAAE,EAAE,YAAY,GAAG,YAAY,EAC/B,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA6H9B"}
|
package/dist/index.js
CHANGED
|
@@ -7480,7 +7480,7 @@ async function elementwiseChainGpuDispatch(ops, xs, options) {
|
|
|
7480
7480
|
if (workgroups > limits.maxComputeWorkgroupsPerDimension) return null;
|
|
7481
7481
|
if (bytes > limits.maxStorageBufferBindingSize) return null;
|
|
7482
7482
|
if (bytes > limits.maxBufferSize) return null;
|
|
7483
|
-
const input = xs instanceof Float32Array ? xs : Float32Array
|
|
7483
|
+
const input = xs instanceof Float32Array ? xs : new Float32Array(xs);
|
|
7484
7484
|
device.pushErrorScope("validation");
|
|
7485
7485
|
bufA = pool.acquireStorageBuffer(bytes, "chain-a", true, true);
|
|
7486
7486
|
bufB = pool.acquireStorageBuffer(bytes, "chain-b", true, true);
|
|
@@ -7562,7 +7562,7 @@ function fuseUnaryChain(ops, xs) {
|
|
|
7562
7562
|
return jsChain(ops, xs);
|
|
7563
7563
|
}
|
|
7564
7564
|
function jsChain(ops, xs) {
|
|
7565
|
-
const out = Float64Array
|
|
7565
|
+
const out = new Float64Array(xs);
|
|
7566
7566
|
for (const op of ops) {
|
|
7567
7567
|
const f = SCALAR[op];
|
|
7568
7568
|
for (let i = 0; i < out.length; i++) out[i] = f(out[i]);
|
|
@@ -7570,10 +7570,10 @@ function jsChain(ops, xs) {
|
|
|
7570
7570
|
return out;
|
|
7571
7571
|
}
|
|
7572
7572
|
async function fuseUnaryChainAsync(ops, xs, options) {
|
|
7573
|
+
const gpu = await elementwiseChainGpuDispatch(ops, xs, options);
|
|
7574
|
+
if (gpu) return new Float64Array(gpu);
|
|
7573
7575
|
const wasm = elementwiseChainDispatch(ops, xs);
|
|
7574
7576
|
if (wasm) return wasm;
|
|
7575
|
-
const gpu = await elementwiseChainGpuDispatch(ops, xs, options);
|
|
7576
|
-
if (gpu) return Float64Array.from(gpu);
|
|
7577
7577
|
return jsChain(ops, xs);
|
|
7578
7578
|
}
|
|
7579
7579
|
|
package/dist/typed/fused.d.ts
CHANGED
|
@@ -26,31 +26,49 @@ export declare function fuseUnaryChain(ops: WasmElementwiseOp[], xs: Float64Arra
|
|
|
26
26
|
* `fuseUnaryChain`, because a GPU dispatch is inherently asynchronous and
|
|
27
27
|
* `fuseUnaryChain`'s synchronous signature is public API.
|
|
28
28
|
*
|
|
29
|
-
* Tiers, in order: **
|
|
29
|
+
* Tiers, in order: **GPU (f32, opt-in) → WASM (f64) → JS (f64)**.
|
|
30
30
|
*
|
|
31
|
-
* ### Why
|
|
31
|
+
* ### Why the GPU is tried first — but ONLY when explicitly enabled
|
|
32
32
|
*
|
|
33
|
-
*
|
|
33
|
+
* Chain `sin→exp→tanh→cos`, 5 reps. ONE run, 2026-07-13, Chrome on an NVIDIA
|
|
34
|
+
* Pascal adapter — the same run quoted in the CHANGELOG and the reference docs, so
|
|
35
|
+
* the three tables agree. Pinned by `gpu-vs-wasm.browser.test.ts`:
|
|
34
36
|
*
|
|
35
|
-
* | n | JS
|
|
36
|
-
* | --------- |
|
|
37
|
-
* | 65,536 |
|
|
38
|
-
* | 262,144 |
|
|
39
|
-
* | 1,048,576 |
|
|
37
|
+
* | n | JS | WASM | GPU | GPU vs WASM |
|
|
38
|
+
* | --------- | ------ | ------ | ---------- | ----------- |
|
|
39
|
+
* | 65,536 | 44 ms | 17 ms | **5.2 ms** | **3.2×** |
|
|
40
|
+
* | 262,144 | 185 ms | 63 ms | **7.5 ms** | **8.3×** |
|
|
41
|
+
* | 1,048,576 | 711 ms | 256 ms | **35 ms** | **7.2×** |
|
|
40
42
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
43
|
+
* The GPU is the fastest tier by a wide margin. It is nevertheless **last-resort
|
|
44
|
+
* by default**, because it computes in f32 while every other tier is f64-exact.
|
|
45
|
+
* `enableGpu()` is how a caller consents to that trade: precision for speed. With
|
|
46
|
+
* the flag off — the default — this function is exactly WASM → JS and returns
|
|
47
|
+
* bit-identical f64 results, so opting out costs nothing.
|
|
46
48
|
*
|
|
47
|
-
*
|
|
48
|
-
* the JS scalar pass ~2–2.5×. It engages only when the caller opted in via
|
|
49
|
-
* `enableGpu()`, WASM declined, a device exists, the array clears
|
|
50
|
-
* `GPU_MIN_ELEMENTS`, and every op has a GPU kernel.
|
|
49
|
+
* ### Provenance of these numbers (read before changing the order)
|
|
51
50
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
51
|
+
* This ordering has been wrong twice, both times from a benchmark measuring
|
|
52
|
+
* something other than what it claimed:
|
|
53
|
+
*
|
|
54
|
+
* 1. GPU-first was first adopted on a "2.3-2.9x faster than JS" result. That
|
|
55
|
+
* baseline was pure JS only because a *separate* bug meant WASM never loaded
|
|
56
|
+
* in browsers. Fixing WASM revealed it beat the GPU, so the order was flipped
|
|
57
|
+
* to WASM-first.
|
|
58
|
+
* 2. That flip was also wrong. The GPU figure it rested on was inflated by
|
|
59
|
+
* `Float32Array.from(f64array)` in the dispatch — the generic `Array.from`
|
|
60
|
+
* path, which runs ToNumber per element. Naming the denominators, because they
|
|
61
|
+
* differ: the *conversion* alone was 73x slower (433 ms vs 5.9 ms at n=2^20),
|
|
62
|
+
* which made the *end-to-end dispatch* 12.2x slower (439.80 ms -> 36.06 ms).
|
|
63
|
+
* With that fixed, the GPU wins outright, as above.
|
|
64
|
+
*
|
|
65
|
+
* The lesson both times: a tier's number is only as good as the tier it is
|
|
66
|
+
* compared against. Re-measure ALL THREE tiers in one run before touching this
|
|
67
|
+
* order — `gpu-vs-wasm.browser.test.ts` does exactly that and fails loudly if the
|
|
68
|
+
* ranking changes.
|
|
69
|
+
*
|
|
70
|
+
* (The GPU also wins decisively for compute-bound work like a large matmul — see
|
|
71
|
+
* `gpuMatmul`. It is not merely a memory-bound-work story.)
|
|
54
72
|
*
|
|
55
73
|
* **Precision.** Always returns a `Float64Array`. When the GPU tier runs, the
|
|
56
74
|
* *values* carry f32 precision (~7 significant digits) even though the container
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fused.d.ts","sourceRoot":"","sources":["../../src/typed/fused.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAA+B,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAyB9F;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,EAAE,YAAY,GAAG,YAAY,CAIvF;
|
|
1
|
+
{"version":3,"file":"fused.d.ts","sourceRoot":"","sources":["../../src/typed/fused.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAA+B,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAyB9F;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,iBAAiB,EAAE,EAAE,EAAE,EAAE,YAAY,GAAG,YAAY,CAIvF;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,iBAAiB,EAAE,EACxB,EAAE,EAAE,YAAY,EAChB,OAAO,CAAC,EAAE,eAAe,GACxB,OAAO,CAAC,YAAY,CAAC,CAgBvB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danielsimonjr/mathts-functions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Mathematical functions for MathTS - arithmetic, algebra, trigonometry, statistics, and more",
|
|
5
5
|
"author": "Daniel Simon Jr.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@danielsimonjr/mathts-core": "^0.6.0",
|
|
38
38
|
"@danielsimonjr/mathts-expression": "^0.6.0",
|
|
39
39
|
"@danielsimonjr/mathts-gpu": "^0.1.1",
|
|
40
|
-
"@danielsimonjr/mathts-matrix": "^0.
|
|
40
|
+
"@danielsimonjr/mathts-matrix": "^0.4.0",
|
|
41
41
|
"@danielsimonjr/mathts-parallel": "^0.3.4",
|
|
42
42
|
"bignumber.js": "^9.1.2",
|
|
43
43
|
"complex.js": "^2.2.5",
|