wgblas 0.1.2 → 1.0.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 (55) hide show
  1. package/README.md +3 -0
  2. package/dist/wgblas.browser.js +1078 -37
  3. package/index.d.mts +6 -0
  4. package/index.mjs +6 -0
  5. package/package.json +32 -1
  6. package/src/classes/GpuMatrix.d.mts +85 -0
  7. package/src/classes/GpuMatrix.mjs +91 -0
  8. package/src/classes/GpuVector.d.mts +11 -7
  9. package/src/classes/GpuVector.mjs +31 -5
  10. package/src/dasum/dasum.d.mts +48 -0
  11. package/src/dasum/dasum.mjs +121 -0
  12. package/src/init.mjs +3 -1
  13. package/src/isamax/isamax.mjs +66 -67
  14. package/src/random/random.d.mts +37 -0
  15. package/src/random/random.mjs +17 -0
  16. package/src/sasum/sasum.mjs +55 -51
  17. package/src/saxpy/saxpy.mjs +48 -35
  18. package/src/scopy/scopy.mjs +43 -32
  19. package/src/sdot/sdot.mjs +60 -55
  20. package/src/sgemv/sgemv.d.mts +118 -0
  21. package/src/sgemv/sgemv.mjs +141 -0
  22. package/src/shaders/browser-shaders.mjs +20 -0
  23. package/src/shaders/dasum.wgsl +98 -0
  24. package/src/shaders/f64add.wgsl +281 -0
  25. package/src/shaders/isamax.wgsl +32 -9
  26. package/src/shaders/reduction/sumF64.wgsl +49 -0
  27. package/src/shaders/sasum.wgsl +18 -4
  28. package/src/shaders/sdot.wgsl +18 -4
  29. package/src/shaders/sgemv_n.wgsl +75 -0
  30. package/src/shaders/sgemv_t.wgsl +65 -0
  31. package/src/shaders/snrm2.wgsl +22 -4
  32. package/src/shaders/ssymv.wgsl +69 -0
  33. package/src/shaders/strmv.wgsl +103 -0
  34. package/src/shaders/strsv_apply_inverse.wgsl +47 -0
  35. package/src/shaders/strsv_invert_block.wgsl +109 -0
  36. package/src/shaders/strsv_update.wgsl +75 -0
  37. package/src/snrm2/snrm2.mjs +56 -52
  38. package/src/srot/srot.mjs +57 -41
  39. package/src/srotm/srotm.mjs +54 -38
  40. package/src/sscal/sscal.mjs +43 -32
  41. package/src/sswap/sswap.mjs +49 -34
  42. package/src/ssymv/ssymv.d.mts +109 -0
  43. package/src/ssymv/ssymv.mjs +130 -0
  44. package/src/strmv/strmv.d.mts +109 -0
  45. package/src/strmv/strmv.mjs +132 -0
  46. package/src/strsv/strsv.d.mts +98 -0
  47. package/src/strsv/strsv.mjs +212 -0
  48. package/src/util/benchmark.mjs +1 -1
  49. package/src/util/bindgroup.mjs +14 -10
  50. package/src/util/buffer.mjs +7 -2
  51. package/src/util/compute.mjs +41 -15
  52. package/src/util/f64pack.mjs +152 -0
  53. package/src/util/pipeline.mjs +32 -17
  54. package/src/util/result.mjs +8 -4
  55. package/src/util/workgroup.mjs +10 -10
package/index.d.mts CHANGED
@@ -2,6 +2,7 @@
2
2
  * @module docs
3
3
  */
4
4
  export { GpuVector } from "./src/classes/GpuVector.mjs";
5
+ export { GpuMatrix } from "./src/classes/GpuMatrix.mjs";
5
6
  export {
6
7
  randomFloat32Array,
7
8
  randomFloat64Array,
@@ -12,10 +13,15 @@ export { saxpy } from "./src/saxpy/saxpy.mjs";
12
13
  export { scopy } from "./src/scopy/scopy.mjs";
13
14
  export { sdot } from "./src/sdot/sdot.mjs";
14
15
  export { sasum } from "./src/sasum/sasum.mjs";
16
+ export { dasum } from "./src/dasum/dasum.mjs";
15
17
  export { snrm2 } from "./src/snrm2/snrm2.mjs";
16
18
  export { isamax } from "./src/isamax/isamax.mjs";
17
19
  export { srot } from "./src/srot/srot.mjs";
18
20
  export { srotm } from "./src/srotm/srotm.mjs";
21
+ export { sgemv } from "./src/sgemv/sgemv.mjs";
22
+ export { ssymv } from "./src/ssymv/ssymv.mjs";
23
+ export { strmv } from "./src/strmv/strmv.mjs";
24
+ export { strsv } from "./src/strsv/strsv.mjs";
19
25
 
20
26
  /**
21
27
  * Initializes the WebGPU device.
package/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  export { init, cleanup, gpuName } from "./src/init.mjs";
2
2
  export { GpuVector } from "./src/classes/GpuVector.mjs";
3
+ export { GpuMatrix } from "./src/classes/GpuMatrix.mjs";
3
4
  export {
4
5
  randomFloat32Array,
5
6
  randomFloat64Array,
@@ -10,7 +11,12 @@ export { saxpy } from "./src/saxpy/saxpy.mjs";
10
11
  export { scopy } from "./src/scopy/scopy.mjs";
11
12
  export { sdot } from "./src/sdot/sdot.mjs";
12
13
  export { sasum } from "./src/sasum/sasum.mjs";
14
+ export { dasum } from "./src/dasum/dasum.mjs";
13
15
  export { snrm2 } from "./src/snrm2/snrm2.mjs";
14
16
  export { isamax } from "./src/isamax/isamax.mjs";
15
17
  export { srot } from "./src/srot/srot.mjs";
16
18
  export { srotm } from "./src/srotm/srotm.mjs";
19
+ export { sgemv } from "./src/sgemv/sgemv.mjs";
20
+ export { ssymv } from "./src/ssymv/ssymv.mjs";
21
+ export { strmv } from "./src/strmv/strmv.mjs";
22
+ export { strsv } from "./src/strsv/strsv.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wgblas",
3
- "version": "0.1.2",
3
+ "version": "1.0.0",
4
4
  "description": "BLAS on WebGPU",
5
5
  "type": "module",
6
6
  "main": "index.mjs",
@@ -15,6 +15,10 @@
15
15
  "import": "./src/classes/GpuVector.mjs",
16
16
  "types": "./src/classes/GpuVector.d.mts"
17
17
  },
18
+ "./classes/GpuMatrix": {
19
+ "import": "./src/classes/GpuMatrix.mjs",
20
+ "types": "./src/classes/GpuMatrix.d.mts"
21
+ },
18
22
  "./random": {
19
23
  "import": "./src/random/random.mjs",
20
24
  "types": "./src/random/random.d.mts"
@@ -43,6 +47,10 @@
43
47
  "import": "./src/sasum/sasum.mjs",
44
48
  "types": "./src/sasum/sasum.d.mts"
45
49
  },
50
+ "./dasum": {
51
+ "import": "./src/dasum/dasum.mjs",
52
+ "types": "./src/dasum/dasum.d.mts"
53
+ },
46
54
  "./snrm2": {
47
55
  "import": "./src/snrm2/snrm2.mjs",
48
56
  "types": "./src/snrm2/snrm2.d.mts"
@@ -58,6 +66,22 @@
58
66
  "./srotm": {
59
67
  "import": "./src/srotm/srotm.mjs",
60
68
  "types": "./src/srotm/srotm.d.mts"
69
+ },
70
+ "./sgemv": {
71
+ "import": "./src/sgemv/sgemv.mjs",
72
+ "types": "./src/sgemv/sgemv.d.mts"
73
+ },
74
+ "./ssymv": {
75
+ "import": "./src/ssymv/ssymv.mjs",
76
+ "types": "./src/ssymv/ssymv.d.mts"
77
+ },
78
+ "./strmv": {
79
+ "import": "./src/strmv/strmv.mjs",
80
+ "types": "./src/strmv/strmv.d.mts"
81
+ },
82
+ "./strsv": {
83
+ "import": "./src/strsv/strsv.mjs",
84
+ "types": "./src/strsv/strsv.d.mts"
61
85
  }
62
86
  },
63
87
  "files": [
@@ -89,17 +113,24 @@
89
113
  "webgpu": "^0.4.0"
90
114
  },
91
115
  "devDependencies": {
116
+ "@commitlint/cli": "^21.2.1",
117
+ "@commitlint/config-conventional": "^21.2.0",
92
118
  "@eslint/js": "^10.0.1",
119
+ "@stdlib/blas-base-dasum": "^0.4.1",
93
120
  "@stdlib/blas-base-isamax": "^0.1.1",
94
121
  "@stdlib/blas-base-sasum": "^0.3.1",
95
122
  "@stdlib/blas-base-saxpy": "^0.3.1",
96
123
  "@stdlib/blas-base-scopy": "^0.3.1",
97
124
  "@stdlib/blas-base-sdot": "^0.3.1",
125
+ "@stdlib/blas-base-sgemv": "^0.1.1",
98
126
  "@stdlib/blas-base-snrm2": "^0.3.1",
99
127
  "@stdlib/blas-base-srot": "^0.2.1",
100
128
  "@stdlib/blas-base-srotm": "^0.2.1",
101
129
  "@stdlib/blas-base-sscal": "^0.3.1",
102
130
  "@stdlib/blas-base-sswap": "^0.3.1",
131
+ "@stdlib/blas-base-ssymv": "^0.1.1",
132
+ "@stdlib/blas-base-strmv": "^0.1.0",
133
+ "@stdlib/blas-base-strsv": "^0.1.1",
103
134
  "@stdlib/random-array-uniform": "^0.2.2",
104
135
  "@stdlib/random-base-uniform": "^0.2.3",
105
136
  "esbuild": "^0.28.1",
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Represents a row-major Float32Array matrix stored in GPU memory.
3
+ *
4
+ * The buffer holds `rows * lda` elements. `lda` (leading dimension) is the
5
+ * number of floats between the start of consecutive rows — must be >= `cols`.
6
+ * When `lda === cols` the matrix is dense with no padding.
7
+ *
8
+ * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/classes/GpuMatrix.mjs#L7">Source code: GpuMatrix.mjs (L7)</a>
9
+ * @see [MDN: GPUBuffer](https://developer.mozilla.org/en-US/docs/Web/API/GPUBuffer)
10
+ * @category Classes
11
+ */
12
+ export declare class GpuMatrix {
13
+ private constructor();
14
+
15
+ /** @internal */
16
+ readonly _buf: GPUBuffer;
17
+
18
+ /** Number of rows. */
19
+ readonly rows: number;
20
+
21
+ /** Number of columns. */
22
+ readonly cols: number;
23
+
24
+ /** Leading dimension — stride between row starts (>= cols). */
25
+ readonly lda: number;
26
+
27
+ /**
28
+ * Uploads a row-major Float32Array or Float64Array matrix to GPU memory. A
29
+ * Float64Array is packed as two f32s per element (WGSL has no f64 type)
30
+ * and stored across two GPU buffers internally; `read()` reassembles the
31
+ * original doubles.
32
+ *
33
+ * `lda` defaults to `cols` (dense, no padding between rows).
34
+ * `data` must have at least `rows * lda` elements.
35
+ *
36
+ * @param data - matrix data in row-major order
37
+ * @param rows - number of rows
38
+ * @param cols - number of columns
39
+ * @param lda - leading dimension (default: cols)
40
+ *
41
+ * @example
42
+ * ```js
43
+ * import { init, GpuMatrix } from "wgblas";
44
+ *
45
+ * await init();
46
+ * // 2×3 matrix: [[1,2,3],[4,5,6]]
47
+ * const mat = GpuMatrix.from(new Float32Array([1,2,3,4,5,6]), 2, 3);
48
+ * console.log(mat.rows, mat.cols, mat.lda); // 2 3 3
49
+ * ```
50
+ */
51
+ static from(data: Float32Array | Float64Array, rows: number, cols: number, lda?: number): GpuMatrix;
52
+
53
+ /**
54
+ * Downloads the matrix from GPU memory and returns a dense row-major
55
+ * array of shape `rows × cols` — a `Float32Array`, or a `Float64Array` if
56
+ * this matrix was created from one. If `lda > cols`, the leading-dimension
57
+ * padding is stripped so the returned array is always tightly packed.
58
+ *
59
+ * @example
60
+ * ```js
61
+ * import { init, GpuMatrix } from "wgblas";
62
+ *
63
+ * await init();
64
+ * const mat = GpuMatrix.from(new Float32Array([1,2,3,4,5,6]), 2, 3);
65
+ * const data = await mat.read();
66
+ * console.log(data); // Float32Array [1, 2, 3, 4, 5, 6]
67
+ * ```
68
+ */
69
+ read(): Promise<Float32Array | Float64Array>;
70
+
71
+ /**
72
+ * Destroys the underlying GPU buffer. Call when the matrix is no longer
73
+ * needed to free GPU memory.
74
+ *
75
+ * @example
76
+ * ```js
77
+ * import { init, GpuMatrix } from "wgblas";
78
+ *
79
+ * await init();
80
+ * const mat = GpuMatrix.from(new Float32Array([1,2,3,4,5,6]), 2, 3);
81
+ * mat.destroy();
82
+ * ```
83
+ */
84
+ destroy(): void;
85
+ }
@@ -0,0 +1,91 @@
1
+ import { getDevice } from "../init.mjs";
2
+ import { uploadBuffer, stageReadback } from "../util/buffer.mjs";
3
+ import { extractResult } from "../util/result.mjs";
4
+ import { packF64, unpackF64 } from "../util/f64pack.mjs";
5
+
6
+ export class GpuMatrix {
7
+ constructor(buffer, rows, cols, lda, auxBuffer = null) {
8
+ this._buf = buffer;
9
+ // Non-null only for Float64Array-backed matrices — see GpuVector for why
10
+ // (packF64 splits each element into a "main"/_buf f32 and "aux"/_auxBuf
11
+ // raw u32 — never a Float32Array, see f64pack.mjs).
12
+ this._auxBuf = auxBuffer;
13
+ this.rows = rows;
14
+ this.cols = cols;
15
+ this.lda = lda;
16
+ }
17
+
18
+ /**
19
+ * Uploads a row-major Float32Array or Float64Array matrix to GPU memory.
20
+ * lda defaults to cols (dense, no padding between rows).
21
+ * data must have at least rows * lda elements.
22
+ */
23
+ static from(data, rows, cols, lda = cols) {
24
+ if (!(data instanceof Float32Array) && !(data instanceof Float64Array))
25
+ throw new Error("GpuMatrix.from expects a Float32Array or Float64Array.");
26
+ if (!Number.isInteger(rows) || rows <= 0)
27
+ throw new Error("rows must be a positive integer.");
28
+ if (!Number.isInteger(cols) || cols <= 0)
29
+ throw new Error("cols must be a positive integer.");
30
+ if (!Number.isInteger(lda) || lda < cols)
31
+ throw new Error("lda must be an integer >= cols.");
32
+ if (data.length < rows * lda)
33
+ throw new Error(
34
+ "data does not have enough elements for the given rows and lda.",
35
+ );
36
+
37
+ if (data instanceof Float64Array) {
38
+ const n = rows * lda;
39
+ const main = new Float32Array(n);
40
+ const aux = new Uint32Array(n);
41
+ for (let i = 0; i < n; i++) {
42
+ const packed = packF64(data[i]);
43
+ main[i] = packed[0];
44
+ aux[i] = packed[1];
45
+ }
46
+ const mainBuf = uploadBuffer(main, "gpu-matrix-f64-main", true);
47
+ const auxBuf = uploadBuffer(aux, "gpu-matrix-f64-aux", true);
48
+ return new GpuMatrix(mainBuf, rows, cols, lda, auxBuf);
49
+ }
50
+
51
+ const buf = uploadBuffer(data.subarray(0, rows * lda), "gpu-matrix", true);
52
+ return new GpuMatrix(buf, rows, cols, lda);
53
+ }
54
+
55
+ async read() {
56
+ const device = getDevice();
57
+ const enc = device.createCommandEncoder();
58
+ const rb = stageReadback(enc, this._buf);
59
+ device.queue.submit([enc.finish()]);
60
+
61
+ if (this._auxBuf) {
62
+ const encAux = device.createCommandEncoder();
63
+ const rbAux = stageReadback(encAux, this._auxBuf);
64
+ device.queue.submit([encAux.finish()]);
65
+
66
+ const [main, aux] = await Promise.all([
67
+ extractResult(rb, Float32Array),
68
+ extractResult(rbAux, Uint32Array),
69
+ ]);
70
+ const raw = new Float64Array(this.rows * this.lda);
71
+ for (let i = 0; i < raw.length; i++) raw[i] = unpackF64(main[i], aux[i]);
72
+ if (this.lda === this.cols) return raw;
73
+ const out = new Float64Array(this.rows * this.cols);
74
+ for (let r = 0; r < this.rows; r++)
75
+ out.set(raw.subarray(r * this.lda, r * this.lda + this.cols), r * this.cols);
76
+ return out;
77
+ }
78
+
79
+ const raw = await extractResult(rb, Float32Array);
80
+ if (this.lda === this.cols) return raw;
81
+ const out = new Float32Array(this.rows * this.cols);
82
+ for (let r = 0; r < this.rows; r++)
83
+ out.set(raw.subarray(r * this.lda, r * this.lda + this.cols), r * this.cols);
84
+ return out;
85
+ }
86
+
87
+ destroy() {
88
+ this._buf.destroy();
89
+ if (this._auxBuf) this._auxBuf.destroy();
90
+ }
91
+ }
@@ -12,12 +12,13 @@ export declare class GpuVector {
12
12
  /** Number of elements in the vector. */
13
13
  readonly length: number;
14
14
 
15
- // TODO: widen to Float32ArrayConstructor | Float64ArrayConstructor when Float64 support is added
16
15
  /** Typed array constructor used when reading data back from the GPU. */
17
- readonly dtype: Float32ArrayConstructor;
16
+ readonly dtype: Float32ArrayConstructor | Float64ArrayConstructor;
18
17
 
19
18
  /**
20
- * Uploads a Float32Array to GPU memory.
19
+ * Uploads a Float32Array or Float64Array to GPU memory. A Float64Array is
20
+ * packed as two f32s per element (WGSL has no f64 type) and stored across
21
+ * two GPU buffers internally; `read()` reassembles the original doubles.
21
22
  *
22
23
  * @param data - input vector data
23
24
  * @returns GpuVector backed by a GPU buffer
@@ -29,15 +30,18 @@ export declare class GpuVector {
29
30
  * await init();
30
31
  * const vec = GpuVector.from(new Float32Array([1, 2, 3, 4]));
31
32
  * console.log("length:", vec.length, "dtype:", vec.dtype.name);
33
+ *
34
+ * const dvec = GpuVector.from(new Float64Array([1.1, 2.2, 3.3]));
35
+ * console.log("dtype:", dvec.dtype.name); // Float64Array
32
36
  * ```
33
37
  */
34
- static from(data: Float32Array): GpuVector;
38
+ static from(data: Float32Array | Float64Array): GpuVector;
35
39
 
36
- // TODO: return type will widen to Promise<Float32Array | Float64Array> when Float64 support is added
37
40
  /**
38
41
  * Reads the vector data back from GPU memory.
39
42
  *
40
- * @returns vector data as a Float32Array
43
+ * @returns vector data as a Float32Array, or a Float64Array if this vector
44
+ * was created from one
41
45
  *
42
46
  * @example
43
47
  * ```js
@@ -49,7 +53,7 @@ export declare class GpuVector {
49
53
  * console.log(data);
50
54
  * ```
51
55
  */
52
- read(): Promise<Float32Array>;
56
+ read(): Promise<Float32Array | Float64Array>;
53
57
 
54
58
  /**
55
59
  * Destroys the underlying GPU buffer. Call when the vector is no longer needed
@@ -1,19 +1,31 @@
1
1
  import { getDevice } from "../init.mjs";
2
2
  import { uploadBuffer, stageReadback } from "../util/buffer.mjs";
3
3
  import { extractResult } from "../util/result.mjs";
4
-
5
- //TODO: Need to update this when we support Float64Array.
4
+ import { packF64, unpackF64 } from "../util/f64pack.mjs";
6
5
 
7
6
  export class GpuVector {
8
- constructor(buffer, length, dtype = Float32Array) {
7
+ constructor(buffer, length, dtype = Float32Array, auxBuffer = null) {
9
8
  this._buf = buffer;
9
+ this._auxBuf = auxBuffer;
10
10
  this.length = length;
11
11
  this.dtype = dtype;
12
12
  }
13
13
 
14
14
  static from(data) {
15
+ if (data instanceof Float64Array) {
16
+ const main = new Float32Array(data.length);
17
+ const aux = new Uint32Array(data.length);
18
+ for (let i = 0; i < data.length; i++) {
19
+ const packed = packF64(data[i]);
20
+ main[i] = packed[0];
21
+ aux[i] = packed[1];
22
+ }
23
+ const mainBuf = uploadBuffer(main, "gpu-vector-f64-main", true);
24
+ const auxBuf = uploadBuffer(aux, "gpu-vector-f64-aux", true);
25
+ return new GpuVector(mainBuf, data.length, Float64Array, auxBuf);
26
+ }
15
27
  if (!(data instanceof Float32Array)) {
16
- throw new Error("GpuVector.from expects a Float32Array.");
28
+ throw new Error("GpuVector.from expects a Float32Array or Float64Array.");
17
29
  }
18
30
  const buf = uploadBuffer(data, "gpu-vector", true);
19
31
  return new GpuVector(buf, data.length, data.constructor);
@@ -24,10 +36,24 @@ export class GpuVector {
24
36
  const enc = device.createCommandEncoder();
25
37
  const rb = stageReadback(enc, this._buf);
26
38
  device.queue.submit([enc.finish()]);
27
- return extractResult(rb, this.dtype);
39
+
40
+ if (!this._auxBuf) return extractResult(rb, this.dtype);
41
+
42
+ const encAux = device.createCommandEncoder();
43
+ const rbAux = stageReadback(encAux, this._auxBuf);
44
+ device.queue.submit([encAux.finish()]);
45
+
46
+ const [main, aux] = await Promise.all([
47
+ extractResult(rb, Float32Array),
48
+ extractResult(rbAux, Uint32Array),
49
+ ]);
50
+ const out = new Float64Array(this.length);
51
+ for (let i = 0; i < this.length; i++) out[i] = unpackF64(main[i], aux[i]);
52
+ return out;
28
53
  }
29
54
 
30
55
  destroy() {
31
56
  this._buf.destroy();
57
+ if (this._auxBuf) this._auxBuf.destroy();
32
58
  }
33
59
  }
@@ -0,0 +1,48 @@
1
+ import { GpuVector } from "../classes/GpuVector.mjs";
2
+
3
+ /**
4
+ * Computes the sum of absolute values of a vector of doubles in double
5
+ * precision: result = sum(|x[i]|). Each element of `x` is packed into a
6
+ * [main, aux] f32 pair (see `packF64`/`GpuVector`) since WGSL has no f64
7
+ * type; accumulation is done with f64add.wgsl's IEEE-754 binary64 addition.
8
+ *
9
+ * {@includeCode ../../examples/dasum/dasum.js}
10
+ *
11
+ * **Browser (standalone HTML):**
12
+ * {@includeCode ../../examples/dasum/web/dasum.html}
13
+ *
14
+ * @param device - GPUDevice from `init()`
15
+ * @param n - number of elements (must be a positive integer)
16
+ * @param x - Float64Array input vector
17
+ * @param incx - stride for x (must be a positive integer)
18
+ * @returns absolute sum scalar — always a CPU readback, even for GpuVector inputs
19
+ * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/dasum/dasum.mjs#L18">Source code: dasum.mjs (L18)</a>
20
+ * @category BLAS Level 1
21
+ */
22
+ export declare function dasum(
23
+ device: GPUDevice,
24
+ n: number,
25
+ x: Float64Array,
26
+ incx: number,
27
+ ): Promise<{ asum: number } | { asum: number; gpuTimeMs: number }>;
28
+
29
+ /**
30
+ * Computes the sum of absolute values of a vector of doubles in double
31
+ * precision: result = sum(|x[i]|).
32
+ *
33
+ * {@includeCode ../../examples/dasum/gpuvec.dasum.js}
34
+ *
35
+ * @param device - GPUDevice from `init()`
36
+ * @param n - number of elements (must be a positive integer)
37
+ * @param x - Float64Array-backed GpuVector input vector
38
+ * @param incx - stride for x (must be a positive integer)
39
+ * @returns absolute sum scalar — always a CPU readback, even for GpuVector inputs
40
+ * @see <a href="https://github.com/manit2004/wgblas/blob/main/src/dasum/dasum.mjs#L18">Source code: dasum.mjs (L18)</a>
41
+ * @category BLAS Level 1
42
+ */
43
+ export declare function dasum(
44
+ device: GPUDevice,
45
+ n: number,
46
+ x: GpuVector,
47
+ incx: number,
48
+ ): Promise<{ asum: number } | { asum: number; gpuTimeMs: number }>;
@@ -0,0 +1,121 @@
1
+ import {
2
+ createStorageBuffer,
3
+ createParamsBuffer,
4
+ createResultBuffer,
5
+ stageReadback,
6
+ destroyBuffers,
7
+ } from "../util/buffer.mjs";
8
+ import { createBindGroup } from "../util/bindgroup.mjs";
9
+ import { runComputePass, submit } from "../util/compute.mjs";
10
+ import { extractTimestamp } from "../util/benchmark.mjs";
11
+ import { extractResult } from "../util/result.mjs";
12
+ import { getPipeline } from "../util/pipeline.mjs";
13
+ import { unpackF64 } from "../util/f64pack.mjs";
14
+ import { GpuVector } from "../classes/GpuVector.mjs";
15
+
16
+ const WGS = 64; // workgroup size
17
+
18
+ export async function dasum(device, n, x, incx) {
19
+ const xIsGpu = x instanceof GpuVector;
20
+
21
+ if (!(device instanceof GPUDevice))
22
+ throw new Error("device must be a GPUDevice.");
23
+ if (!Number.isInteger(n) || !Number.isInteger(incx))
24
+ throw new Error("n and incx must be integers.");
25
+ if (incx <= 0) throw new Error("incx must be positive.");
26
+ if (!xIsGpu && !(x instanceof Float64Array))
27
+ throw new Error("x must be a Float64Array or GpuVector.");
28
+ if (xIsGpu && x.dtype !== Float64Array)
29
+ throw new Error("x must be a Float64Array-backed GpuVector.");
30
+ if (n <= 0) return { asum: 0 };
31
+ if (x.length < (n - 1) * incx + 1)
32
+ throw new Error(
33
+ "x does not have enough elements for the given n and incx.",
34
+ );
35
+
36
+ // dasum.wgsl/reduction/sumF64.wgsl are each concatenated with f64add.wgsl
37
+ // (reusing its decode/encode/computeSum/Packed — WGSL has no #include).
38
+ // f64add.wgsl declares no bindings/entry point of its own, so each
39
+ // concatenated module has exactly one @compute entry — omitting entryPoint
40
+ // here lets getPipeline auto-detect it, the stable path (see pipeline.mjs).
41
+ const pipelineMain = await getPipeline(device, ["f64add", "dasum"]);
42
+ const pipelineReduce = await getPipeline(device, ["f64add", "reduction/sumF64"]);
43
+
44
+ let xVec = null;
45
+ let partialsMainBuffer = null;
46
+ let partialsAuxBuffer = null;
47
+ let resultMainBuffer = null;
48
+ let resultAuxBuffer = null;
49
+ let paramsBuffer = null;
50
+ let readMainBuffer = null;
51
+ let readAuxBuffer = null;
52
+
53
+ try {
54
+ xVec = xIsGpu ? x : GpuVector.from(x);
55
+ partialsMainBuffer = createStorageBuffer(2 * WGS * 4, "dasum-partialsMain"); // 2*WGS partial sums, main halves
56
+ partialsAuxBuffer = createStorageBuffer(2 * WGS * 4, "dasum-partialsAux"); // 2*WGS partial sums, aux halves (raw u32 bits)
57
+ resultMainBuffer = createResultBuffer(4, "dasum-result-main"); // final main half
58
+ resultAuxBuffer = createResultBuffer(4, "dasum-result-aux"); // final aux half (raw u32 bits)
59
+ paramsBuffer = createParamsBuffer(
60
+ [
61
+ { value: n, type: "u32" },
62
+ { value: incx, type: "u32" },
63
+ ],
64
+ "dasum-params",
65
+ );
66
+
67
+ const bgMain = createBindGroup(
68
+ pipelineMain.getBindGroupLayout(0),
69
+ [xVec._buf, xVec._auxBuf, partialsMainBuffer, partialsAuxBuffer, paramsBuffer],
70
+ );
71
+ const { commandEncoder: enc1, ts: ts1 } = runComputePass(
72
+ pipelineMain,
73
+ bgMain,
74
+ 2 * WGS,
75
+ ); // dispatch 2*WGS workgroups
76
+
77
+ submit(enc1);
78
+
79
+ const bgReduce = createBindGroup(
80
+ pipelineReduce.getBindGroupLayout(0),
81
+ [partialsMainBuffer, partialsAuxBuffer, resultMainBuffer, resultAuxBuffer],
82
+ );
83
+ const { commandEncoder: enc2, ts: ts2 } = runComputePass(
84
+ pipelineReduce,
85
+ bgReduce,
86
+ 1,
87
+ ); // dispatch 1 workgroup to reduce the partial sums to a single result
88
+ readMainBuffer = stageReadback(enc2, resultMainBuffer);
89
+ readAuxBuffer = stageReadback(enc2, resultAuxBuffer);
90
+
91
+ submit(enc2);
92
+
93
+ const mainPromise = extractResult(readMainBuffer, Float32Array);
94
+ const auxPromise = extractResult(readAuxBuffer, Uint32Array);
95
+ readMainBuffer = null; // ownership transferred — extractResult's own finally destroys it
96
+ readAuxBuffer = null;
97
+
98
+ const [gpuTime1, gpuTime2, mainArr, auxArr] = await Promise.all([
99
+ extractTimestamp(ts1),
100
+ extractTimestamp(ts2),
101
+ mainPromise,
102
+ auxPromise,
103
+ ]);
104
+
105
+ // asum is always a scalar readback — both paths return { asum }
106
+ const asum = unpackF64(mainArr[0], auxArr[0]);
107
+ if (gpuTime1 !== undefined && gpuTime2 !== undefined)
108
+ return { asum, gpuTimeMs: gpuTime1 + gpuTime2 };
109
+ return { asum };
110
+ } finally {
111
+ if (!xIsGpu && xVec) xVec.destroy();
112
+ if (partialsMainBuffer) destroyBuffers(partialsMainBuffer);
113
+ if (partialsAuxBuffer) destroyBuffers(partialsAuxBuffer);
114
+ if (resultMainBuffer) destroyBuffers(resultMainBuffer);
115
+ if (resultAuxBuffer) destroyBuffers(resultAuxBuffer);
116
+ if (paramsBuffer) destroyBuffers(paramsBuffer);
117
+ // Only reached if submit(enc2) threw before ownership was transferred above.
118
+ if (readMainBuffer) destroyBuffers(readMainBuffer);
119
+ if (readAuxBuffer) destroyBuffers(readAuxBuffer);
120
+ }
121
+ }
package/src/init.mjs CHANGED
@@ -41,7 +41,9 @@ export async function init({
41
41
  }
42
42
 
43
43
  _benchmarkEnabled = benchmark;
44
- _device = await _adapter.requestDevice(benchmarkMode(_adapter, benchmark));
44
+ const bmConfig = benchmarkMode(_adapter, benchmark);
45
+ const features = [...(bmConfig.requiredFeatures ?? [])];
46
+ _device = await _adapter.requestDevice({ requiredFeatures: features });
45
47
  // Fires for any GPU error not caught by a pushErrorScope/popErrorScope pair — surfaces silent GPU failures to the console.
46
48
  // See: https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/uncapturederror_event
47
49
  _device.addEventListener("uncapturederror", (e) => {