/** * WebGPU fused element-wise chain. * * Why a *chain* and not a single op: a lone element-wise op on the GPU is pure * transfer tax — upload n floats, do one flop each, read n floats back. A * **fused chain** uploads once, runs every op on-device by ping-ponging two * storage buffers, and reads back once, so the transfer is amortized across the * whole chain. * * **Read this before reaching for the GPU:** for element-wise chains the GPU is * the *fastest* tier (3.2–8.3× over WASM — see the table on * `fuseUnaryChainAsync`), but it computes in **f32** where every CPU tier is * f64-exact. That is the whole trade, and `enableGpu()` is how a caller consents * to it. `fuseUnaryChainAsync` therefore tries the GPU first, but only when the * flag is on; with it off (the default) the GPU never runs. * * An earlier revision of this comment claimed the GPU was ~1.9× *slower* than * WASM. That was an artifact of a `Float32Array.from()` in this very file — the * generic `Array.from` path, which cost 433 ms at n=2²⁰ where the constructor * costs 5.9 ms. Do not re-derive a tier ranking from a single tier's number; see * `gpu-vs-wasm.browser.test.ts`, which measures all three in one run. * * Contract (mirrors the WASM `elementwiseChainDispatch`): a **never-throw** * best-effort fast path. It returns `null` — never rejects — whenever the GPU is * unavailable, not opted in, the input is too small, or the chain contains an op * with no GPU kernel. The caller then falls through to the CPU tiers. */ import { type GPUContextOptions } from '@danielsimonjr/mathts-gpu'; declare const WGSL_OP_BODY: { readonly abs: 'return abs(x);'; readonly sin: 'return sin(x);'; readonly cos: 'return cos(x);'; readonly tan: 'return tan(x);'; readonly exp: 'return exp(x);'; readonly atan: 'return atan(x);'; readonly sinh: 'return sinh(x);'; readonly tanh: 'return tanh(x);'; readonly log: 'return safe_log(x);'; readonly log2: 'return safe_log(x) * 1.4426950408889634;'; readonly log10: 'return safe_log(x) * 0.4342944819032518;'; readonly atanh: 'return safe_atanh(x);'; readonly sec: 'return safe_recip(cos(x));'; readonly csc: 'return safe_recip(sin(x));'; readonly cot: 'return safe_recip(tan(x));'; }; /** Ops that have a GPU kernel. A chain outside this set falls back. */ export type GpuElementwiseOp = keyof typeof WGSL_OP_BODY; export declare const GPU_ELEMENTWISE_OPS: readonly GpuElementwiseOp[]; /** Whether every op in the chain has a GPU kernel. */ export declare function isGpuChainSupported(ops: readonly string[]): ops is readonly GpuElementwiseOp[]; /** Reductions that can be fused onto the end of a chain. */ export type GpuReduceOp = 'sum' | 'max' | 'min'; export declare const GPU_REDUCE_OPS: readonly ["sum", "max", "min"]; /** Drop the cached shaders/buffers (device loss, or between tests). */ export declare function resetGpuElementwise(): void; /** Options for a GPU element-wise dispatch. */ export interface GpuChainOptions extends GPUContextOptions { /** * Per-call override of the global `enableGpu()` flag. * * The global flag is process-wide mutable state: any dependency that calls * `enableGpu()` would otherwise change *your* call's behaviour. Passing `gpu` * explicitly makes a call self-describing and immune to that. */ gpu?: boolean; } /** * Run a fused element-wise chain on the GPU. * * @param ops - the chain, applied left to right (`['sin','exp']` = `exp(sin(x))`) * @param xs - input samples * @returns the f32 results, or `null` to signal "fall back to another tier" */ export declare function elementwiseChainGpuDispatch(ops: readonly string[], xs: Float64Array | Float32Array, options?: GpuChainOptions): Promise; /** * Apply `ops` on the GPU and **reduce the result on-device**, returning a single * number instead of an array. * * The point is the readback, not the arithmetic: reducing on the device replaces * an **n-float** transfer back to the CPU with an **n/256-float** one. Measured * end-to-end through THIS function (not a prototype), NVIDIA Pascal, * `sum(exp(sin(x)))`: * * | n | WASM chain + JS sum | GPU chain + JS sum | fused GPU reduce | * | --------- | ------------------- | ------------------ | ---------------- | * | 262,144 | 25.6 ms | 16.7 ms | **9.9 ms** | * | 1,048,576 | 96.8 ms | 34.3 ms | **25.4 ms** | * | 4,194,304 | 260.0 ms | 100.0 ms | **72.2 ms** | * * **1.35-1.7x** over the shipped GPU path, **2.6-3.8x** over the CPU tier. * * Quote the **1.39x at n=2^22** if you quote one number: it is the only ratio here that * reproduces run to run (1.31-1.39x over four runs). The 1.7x is the n=262,144 row, and * that size swings 1.19-2.83x between runs — the GPU work is short enough that fixed * costs dominate. A headline should not be a lucky sample. * * Why not more: a bare-WGSL prototype of this hit ~2x, but it pre-converted its * input outside the timed region. The real f64->f32 conversion is an n-scaling cost * that BOTH paths pay, so it dilutes the ratio as n grows (the absolute saving is * steady: ~28 ms at n=2^22). The prototype's number was not a lie, it was measuring * a workload no caller has. Quote the numbers above, not those. * * **An empty `ops` is declined on purpose.** A *standalone* GPU reduction uploads * n floats to produce one number — pure transfer tax, measured 3-9x SLOWER than a * plain JS sum. There is no chain to amortise the upload against, so this returns * `null` and lets the caller use the CPU, which is genuinely the faster path. The * upload is only worth paying for when real work rides along with it. * * Same never-throw contract as {@link elementwiseChainGpuDispatch}: returns `null` * — never rejects — whenever the GPU is unavailable, not opted into, the input is * below `GPU_MIN_ELEMENTS`, an op has no kernel, or a device limit is exceeded. * * Precision: f32, like every GPU path here. For `sum` the tree reduction is * pairwise, so its error grows O(log n) rather than the O(n) of a sequential * accumulate — it is better-conditioned than the JS loop it replaces, even though * it works in f32. */ export declare function elementwiseChainReduceGpuDispatch(ops: readonly string[], xs: Float64Array | Float32Array, reduce: GpuReduceOp, options?: GpuChainOptions): Promise; export {}; //# sourceMappingURL=elementwise-gpu.d.ts.map