wgblas 2.0.0 → 2.1.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 +18 -18
- package/dist/wgblas.browser.js +1273 -1239
- package/index.d.mts +38 -6
- package/package.json +2 -1
- package/src/classes/GpuMatrix.mjs +17 -10
- package/src/classes/GpuVector.mjs +28 -10
- package/src/dasum/dasum.d.mts +4 -4
- package/src/dasum/dasum.mjs +19 -17
- package/src/devdocs.mjs +13 -0
- package/src/idamax/idamax.d.mts +20 -2
- package/src/idamax/idamax.mjs +18 -16
- package/src/init.mjs +114 -56
- package/src/isamax/isamax.d.mts +20 -2
- package/src/isamax/isamax.mjs +16 -14
- package/src/random/random.d.mts +1 -0
- package/src/sasum/sasum.d.mts +2 -2
- package/src/sasum/sasum.mjs +15 -13
- package/src/saxpy/saxpy.d.mts +2 -2
- package/src/saxpy/saxpy.mjs +10 -8
- package/src/scopy/scopy.d.mts +2 -2
- package/src/scopy/scopy.mjs +10 -8
- package/src/sdot/sdot.d.mts +2 -2
- package/src/sdot/sdot.mjs +16 -14
- package/src/sgemm/sgemm.mjs +28 -15
- package/src/sgemmtr/sgemmtr.mjs +16 -15
- package/src/sgemv/sgemv.mjs +38 -26
- package/src/sger/sger.mjs +10 -8
- package/src/shaders/index.mjs +164 -14
- package/src/shaders/reduction/scaledSum.wgsl +65 -0
- package/src/shaders/sgemm_large.wgsl +107 -18
- package/src/shaders/sgemm_small.wgsl +115 -15
- package/src/shaders/sgemmtr_large.wgsl +4 -1
- package/src/shaders/sgemmtr_small.wgsl +4 -1
- package/src/shaders/sgemv_n.wgsl +3 -1
- package/src/shaders/sgemv_t.wgsl +3 -1
- package/src/shaders/snrm2.wgsl +72 -23
- package/src/shaders/ssymv.wgsl +3 -1
- package/src/snrm2/snrm2.d.mts +2 -2
- package/src/snrm2/snrm2.mjs +33 -21
- package/src/srot/srot.d.mts +2 -4
- package/src/srot/srot.mjs +11 -9
- package/src/srotm/srotm.d.mts +2 -4
- package/src/srotm/srotm.mjs +19 -10
- package/src/sscal/sscal.d.mts +3 -3
- package/src/sscal/sscal.mjs +12 -10
- package/src/sswap/sswap.d.mts +2 -2
- package/src/sswap/sswap.mjs +11 -9
- package/src/ssymm/ssymm.mjs +31 -22
- package/src/ssymv/ssymv.mjs +10 -8
- package/src/ssyr/ssyr.mjs +9 -7
- package/src/ssyr2/ssyr2.mjs +10 -8
- package/src/ssyr2k/ssyr2k.mjs +18 -17
- package/src/ssyrk/ssyrk.mjs +18 -17
- package/src/strmm/strmm.mjs +47 -32
- package/src/strmv/strmv.mjs +10 -8
- package/src/strsm/strsm.mjs +54 -36
- package/src/strsv/strsv.mjs +16 -12
- package/src/util/benchmark.mjs +4 -6
- package/src/util/bindgroup.mjs +1 -3
- package/src/util/buffer.mjs +113 -19
- package/src/util/compute.mjs +6 -9
- package/src/util/constants.mjs +57 -0
- package/src/util/device.mjs +34 -0
- package/src/util/pipeline.mjs +5 -6
- package/src/util/workgroup.mjs +55 -7
- package/src/shaders/browser-shaders.mjs +0 -81
package/src/init.mjs
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import { benchmarkMode } from "./util/benchmark.mjs";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
// One WebGPU instance for the whole process, never released. A GPUAdapter
|
|
4
|
+
// yields at most one working device, so a second device needs a second
|
|
5
|
+
// adapter — but creating a second *instance* aborts Dawn during native
|
|
6
|
+
// teardown (std::system_error), whether the instances overlap or are made one
|
|
7
|
+
// after another. So the instance is built once and every adapter comes from
|
|
8
|
+
// it; cleanup() releases devices, not this.
|
|
9
|
+
let _gpu = null;
|
|
10
|
+
let _dumpShaders = false; // instance-level Dawn toggle, fixed when _gpu is made
|
|
11
|
+
|
|
12
|
+
// Resolved-options key -> GPUDevice. init() returns the cached device for a
|
|
13
|
+
// given option set and creates one per distinct set, so a process can drive
|
|
14
|
+
// several GPUs at once (e.g. discrete via "high-performance", integrated via
|
|
15
|
+
// "low-power").
|
|
16
|
+
const _devices = new Map();
|
|
17
|
+
// GPUDevice -> { adapter, benchmark, options }. Benchmark support is a
|
|
18
|
+
// property of the device (its requiredFeatures), not of the library.
|
|
19
|
+
const _meta = new WeakMap();
|
|
20
|
+
// The device from the first init(); what getDevice() returns for callers that
|
|
21
|
+
// never mention one (GpuVector.from(data), GpuMatrix.from(data, ...)).
|
|
22
|
+
let _primary = null;
|
|
23
|
+
|
|
24
|
+
const optionsKey = ({ powerPreference, benchmark }) => `${powerPreference}::${benchmark}`;
|
|
8
25
|
|
|
9
26
|
// ── Public API ───────────────────────────────────────────────────────────────
|
|
10
27
|
|
|
@@ -13,103 +30,144 @@ export async function init({
|
|
|
13
30
|
benchmark = false,
|
|
14
31
|
dumpShaders = false,
|
|
15
32
|
} = {}) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
33
|
+
const options = { powerPreference, benchmark, dumpShaders };
|
|
34
|
+
const key = optionsKey(options);
|
|
35
|
+
|
|
36
|
+
// Same options: idempotent, hand back the device already built for them.
|
|
37
|
+
const cached = _devices.get(key);
|
|
38
|
+
if (cached) return cached;
|
|
19
39
|
|
|
20
|
-
let gpu;
|
|
21
40
|
// Browser exposes WebGPU natively via navigator.gpu.
|
|
22
41
|
// Node.js has no navigator, so we polyfill using the "webgpu" npm package which also
|
|
23
42
|
// injects WebGPU globals (GPUBufferUsage, GPUShaderStage, etc.) into globalThis.
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
if (!_gpu) {
|
|
44
|
+
if (typeof window === "undefined") {
|
|
45
|
+
const { create, globals } = await import("webgpu");
|
|
46
|
+
Object.assign(globalThis, globals);
|
|
47
|
+
// dumpShaders forwards Dawn's own debug toggle — prints each pipeline's
|
|
48
|
+
// WGSL and compiled backend IR to stderr. Node-only; see index.d.mts.
|
|
49
|
+
const toggles = dumpShaders
|
|
50
|
+
? ["enable-dawn-features=dump_shaders,disable_symbol_renaming"]
|
|
51
|
+
: [];
|
|
52
|
+
_gpu = create(toggles);
|
|
53
|
+
_dumpShaders = dumpShaders;
|
|
54
|
+
} else {
|
|
55
|
+
if (dumpShaders)
|
|
56
|
+
console.warn("dumpShaders has no effect in the browser — see init()'s docs.");
|
|
57
|
+
_gpu = navigator.gpu;
|
|
58
|
+
}
|
|
59
|
+
} else if (dumpShaders !== _dumpShaders && typeof window === "undefined") {
|
|
60
|
+
// Unlike powerPreference and benchmark, dumpShaders is a toggle on the Dawn
|
|
61
|
+
// instance rather than the device, and the instance is shared, so a later
|
|
62
|
+
// init() cannot change it.
|
|
63
|
+
console.warn(
|
|
64
|
+
`dumpShaders: ${dumpShaders} was requested, but the WebGPU instance was already created with ` +
|
|
65
|
+
`dumpShaders: ${_dumpShaders}. The first init() call fixes this for the process.`,
|
|
66
|
+
);
|
|
38
67
|
}
|
|
39
68
|
|
|
40
|
-
if (!
|
|
69
|
+
if (!_gpu) {
|
|
41
70
|
throw new Error("WebGPU not supported in this environment.");
|
|
42
71
|
}
|
|
43
72
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
73
|
+
// A fresh adapter per device: requesting a device consumes its adapter, so
|
|
74
|
+
// reusing one would hand back an already-lost device.
|
|
75
|
+
const adapter =
|
|
76
|
+
(await _gpu.requestAdapter({ powerPreference })) ??
|
|
77
|
+
(await _gpu.requestAdapter());
|
|
78
|
+
if (!adapter) {
|
|
48
79
|
throw new Error("No WebGPU adapter found.");
|
|
49
80
|
}
|
|
50
81
|
|
|
51
|
-
|
|
52
|
-
const bmConfig = benchmarkMode(_adapter, benchmark);
|
|
82
|
+
const bmConfig = benchmarkMode(adapter, benchmark);
|
|
53
83
|
const features = [...(bmConfig.requiredFeatures ?? [])];
|
|
54
|
-
|
|
84
|
+
const device = await adapter.requestDevice({ requiredFeatures: features });
|
|
55
85
|
// Fires for any GPU error not caught by a pushErrorScope/popErrorScope pair — surfaces silent GPU failures to the console.
|
|
56
86
|
// See: https://developer.mozilla.org/en-US/docs/Web/API/GPUDevice/uncapturederror_event
|
|
57
|
-
|
|
87
|
+
device.addEventListener("uncapturederror", (e) => {
|
|
58
88
|
console.error("Uncaptured GPU error:", e.error.message);
|
|
59
89
|
});
|
|
60
90
|
|
|
61
|
-
|
|
91
|
+
// benchmarkMode() drops the feature when the adapter can't do timestamp
|
|
92
|
+
// queries, so record what was actually granted rather than what was asked
|
|
93
|
+
// for — otherwise beginTimestamp() would build a query set on a device that
|
|
94
|
+
// never requested the feature.
|
|
95
|
+
const benchmarkGranted = features.includes("timestamp-query");
|
|
96
|
+
_meta.set(device, { adapter, benchmark: benchmarkGranted, options });
|
|
97
|
+
_devices.set(key, device);
|
|
98
|
+
if (!_primary) _primary = device;
|
|
99
|
+
|
|
100
|
+
return device;
|
|
62
101
|
}
|
|
63
102
|
|
|
64
|
-
export function cleanup() {
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
103
|
+
export function cleanup(device) {
|
|
104
|
+
if (device === undefined) {
|
|
105
|
+
for (const d of _devices.values()) d.destroy();
|
|
106
|
+
_devices.clear();
|
|
107
|
+
_primary = null;
|
|
108
|
+
return;
|
|
68
109
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
110
|
+
|
|
111
|
+
// Releasing one device of several. Unknown or already-released devices are a
|
|
112
|
+
// no-op so teardown paths can call this unguarded.
|
|
113
|
+
const meta = _meta.get(device);
|
|
114
|
+
if (!meta) return;
|
|
115
|
+
_devices.delete(optionsKey(meta.options));
|
|
116
|
+
_meta.delete(device);
|
|
117
|
+
device.destroy();
|
|
118
|
+
|
|
119
|
+
// getDevice() must keep answering while any device is left, so promote a
|
|
120
|
+
// survivor when the primary is the one being released.
|
|
121
|
+
if (_primary === device) _primary = _devices.values().next().value ?? null;
|
|
72
122
|
}
|
|
73
123
|
|
|
74
|
-
export function gpuName() {
|
|
75
|
-
|
|
124
|
+
export function gpuName(device = _primary) {
|
|
125
|
+
const meta = device && _meta.get(device);
|
|
126
|
+
if (!meta) {
|
|
76
127
|
throw new Error("WebGPU adapter not initialized — call init() first.");
|
|
77
128
|
}
|
|
78
|
-
const { device, description } =
|
|
129
|
+
const { device: deviceName, description } = meta.adapter.info;
|
|
79
130
|
return {
|
|
80
131
|
description: description || "unknown",
|
|
81
|
-
device:
|
|
132
|
+
device: deviceName || "unknown",
|
|
82
133
|
};
|
|
83
134
|
}
|
|
84
135
|
|
|
85
136
|
// ── Library internals (not part of the public API) ───────────────────────────
|
|
86
137
|
|
|
87
|
-
/**
|
|
88
|
-
|
|
89
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Whether benchmark mode is active for `device` — i.e. it was created with
|
|
140
|
+
* `benchmark: true` *and* its adapter actually supports timestamp queries.
|
|
141
|
+
* @param {GPUDevice} [device] - defaults to the first-initialized device
|
|
142
|
+
* @returns {boolean}
|
|
143
|
+
*/
|
|
144
|
+
export function isBenchmarkEnabled(device = _primary) {
|
|
145
|
+
return _meta.get(device)?.benchmark ?? false;
|
|
90
146
|
}
|
|
91
147
|
|
|
92
148
|
/**
|
|
93
|
-
* Returns the
|
|
149
|
+
* Returns the device from the first `init()` call — the default for callers
|
|
150
|
+
* that don't name one. Throws if `init()` has not been called.
|
|
94
151
|
* @returns {GPUDevice}
|
|
95
|
-
* @throws {Error} if
|
|
152
|
+
* @throws {Error} if no device is initialized
|
|
96
153
|
*/
|
|
97
154
|
export function getDevice() {
|
|
98
|
-
if (!
|
|
155
|
+
if (!_primary) {
|
|
99
156
|
throw new Error("WebGPU device not initialized — call init() first.");
|
|
100
157
|
}
|
|
101
|
-
return
|
|
158
|
+
return _primary;
|
|
102
159
|
}
|
|
103
160
|
|
|
104
161
|
/**
|
|
105
|
-
* Returns the
|
|
162
|
+
* Returns the `GPUAdapter` backing `device`. Throws if it isn't initialized.
|
|
163
|
+
* @param {GPUDevice} [device] - defaults to the first-initialized device
|
|
106
164
|
* @returns {GPUAdapter}
|
|
107
165
|
* @throws {Error} if the adapter is not initialized
|
|
108
166
|
*/
|
|
109
|
-
export function getAdapter() {
|
|
110
|
-
|
|
167
|
+
export function getAdapter(device = _primary) {
|
|
168
|
+
const meta = device && _meta.get(device);
|
|
169
|
+
if (!meta) {
|
|
111
170
|
throw new Error("WebGPU adapter not initialized — call init() first.");
|
|
112
171
|
}
|
|
113
|
-
return
|
|
172
|
+
return meta.adapter;
|
|
114
173
|
}
|
|
115
|
-
|
package/src/isamax/isamax.d.mts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { GpuVector } from "../classes/GpuVector.mjs";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Returns the 0-based index of the element with the largest absolute value
|
|
4
|
+
* Returns the 0-based index of the element with the largest absolute value:
|
|
5
|
+
* $$\text{index} = \arg\max_{i} |x_i|$$
|
|
5
6
|
* Ties are broken in favour of the lower index, matching CBLAS behaviour.
|
|
6
7
|
*
|
|
8
|
+
* **NaN handling.** The search compares with `>`, which is false for NaN, so
|
|
9
|
+
* NaN elements are skipped rather than selected: a vector of all NaN returns
|
|
10
|
+
* `0`. This matches CBLAS except when `x[0]` itself is NaN — CBLAS seeds its
|
|
11
|
+
* running maximum from `x[0]`, and since no comparison against NaN succeeds it
|
|
12
|
+
* returns `0` however large the later elements are, whereas this returns the
|
|
13
|
+
* index of the largest non-NaN element. `+-Infinity` compares normally and is
|
|
14
|
+
* selected as the maximum.
|
|
15
|
+
*
|
|
7
16
|
* {@includeCode ../../examples/isamax/isamax.js}
|
|
8
17
|
*
|
|
9
18
|
* **Browser (standalone HTML):**
|
|
@@ -25,9 +34,18 @@ export declare function isamax(
|
|
|
25
34
|
): Promise<{ index: number } | { index: number; gpuTimeMs: number }>;
|
|
26
35
|
|
|
27
36
|
/**
|
|
28
|
-
* Returns the 0-based index of the element with the largest absolute value
|
|
37
|
+
* Returns the 0-based index of the element with the largest absolute value:
|
|
38
|
+
* $$\text{index} = \arg\max_{i} |x_i|$$
|
|
29
39
|
* Ties are broken in favour of the lower index, matching CBLAS behaviour.
|
|
30
40
|
*
|
|
41
|
+
* **NaN handling.** The search compares with `>`, which is false for NaN, so
|
|
42
|
+
* NaN elements are skipped rather than selected: a vector of all NaN returns
|
|
43
|
+
* `0`. This matches CBLAS except when `x[0]` itself is NaN — CBLAS seeds its
|
|
44
|
+
* running maximum from `x[0]`, and since no comparison against NaN succeeds it
|
|
45
|
+
* returns `0` however large the later elements are, whereas this returns the
|
|
46
|
+
* index of the largest non-NaN element. `+-Infinity` compares normally and is
|
|
47
|
+
* selected as the maximum.
|
|
48
|
+
*
|
|
31
49
|
* {@includeCode ../../examples/isamax/gpu.isamax.js}
|
|
32
50
|
*
|
|
33
51
|
* @param device - GPUDevice from `init()`
|
package/src/isamax/isamax.mjs
CHANGED
|
@@ -12,14 +12,16 @@ import { extractTimestamp } from "../util/benchmark.mjs";
|
|
|
12
12
|
import { extractResult } from "../util/result.mjs";
|
|
13
13
|
import { getPipeline } from "../util/pipeline.mjs";
|
|
14
14
|
import { GpuVector } from "../classes/GpuVector.mjs";
|
|
15
|
+
import { WGS } from "../util/constants.mjs";
|
|
16
|
+
import { requireSameDevice } from "../util/device.mjs";
|
|
15
17
|
|
|
16
|
-
const WGS = 64;
|
|
17
18
|
|
|
18
19
|
export async function isamax(device, n, x, incx) {
|
|
19
20
|
const xIsGpu = x instanceof GpuVector;
|
|
20
21
|
|
|
21
22
|
if (!(device instanceof GPUDevice))
|
|
22
23
|
throw new Error("device must be a GPUDevice.");
|
|
24
|
+
requireSameDevice(device, "isamax", { x });
|
|
23
25
|
if (!Number.isInteger(n) || !Number.isInteger(incx))
|
|
24
26
|
throw new Error("n and incx must be integers.");
|
|
25
27
|
if (incx <= 0) throw new Error("incx must be positive.");
|
|
@@ -42,17 +44,17 @@ export async function isamax(device, n, x, incx) {
|
|
|
42
44
|
let readBuffer = null;
|
|
43
45
|
|
|
44
46
|
try {
|
|
45
|
-
xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "isamax-x", false);
|
|
46
|
-
partialsValBuffer = createStorageBuffer(
|
|
47
|
+
xBuffer = xIsGpu ? x._buf : uploadBuffer(device, x, "isamax-x", false);
|
|
48
|
+
partialsValBuffer = createStorageBuffer(device,
|
|
47
49
|
2 * WGS * 4,
|
|
48
50
|
"isamax-partials-val",
|
|
49
51
|
); //to hold 2*WGS partial max values of f32
|
|
50
|
-
partialsIdxBuffer = createStorageBuffer(
|
|
52
|
+
partialsIdxBuffer = createStorageBuffer(device,
|
|
51
53
|
2 * WGS * 4,
|
|
52
54
|
"isamax-partials-idx",
|
|
53
55
|
); //to hold 2*WGS partial max indices of u32
|
|
54
|
-
resultBuffer = createResultBuffer(4, "isamax-result"); // u32 index
|
|
55
|
-
paramsBuffer = createParamsBuffer(
|
|
56
|
+
resultBuffer = createResultBuffer(device, 4, "isamax-result"); // u32 index
|
|
57
|
+
paramsBuffer = createParamsBuffer(device,
|
|
56
58
|
[
|
|
57
59
|
{ value: n, type: "u32" },
|
|
58
60
|
{ value: incx, type: "u32" },
|
|
@@ -60,33 +62,33 @@ export async function isamax(device, n, x, incx) {
|
|
|
60
62
|
"isamax-params",
|
|
61
63
|
);
|
|
62
64
|
|
|
63
|
-
const bgMain = createBindGroup(pipelineMain.getBindGroupLayout(0), [
|
|
65
|
+
const bgMain = createBindGroup(device, pipelineMain.getBindGroupLayout(0), [
|
|
64
66
|
xBuffer,
|
|
65
67
|
partialsValBuffer,
|
|
66
68
|
partialsIdxBuffer,
|
|
67
69
|
paramsBuffer,
|
|
68
70
|
]);
|
|
69
|
-
const { commandEncoder: enc1, ts: ts1 } = runComputePass(
|
|
71
|
+
const { commandEncoder: enc1, ts: ts1 } = runComputePass(device,
|
|
70
72
|
pipelineMain,
|
|
71
73
|
bgMain,
|
|
72
74
|
2 * WGS,
|
|
73
75
|
); //dispatch 2*WGS workgroups
|
|
74
76
|
|
|
75
|
-
submit(enc1);
|
|
77
|
+
submit(device, enc1);
|
|
76
78
|
|
|
77
|
-
const bgReduce = createBindGroup(pipelineReduce.getBindGroupLayout(0), [
|
|
79
|
+
const bgReduce = createBindGroup(device, pipelineReduce.getBindGroupLayout(0), [
|
|
78
80
|
partialsValBuffer,
|
|
79
81
|
partialsIdxBuffer,
|
|
80
82
|
resultBuffer,
|
|
81
83
|
]);
|
|
82
|
-
const { commandEncoder: enc2, ts: ts2 } = runComputePass(
|
|
84
|
+
const { commandEncoder: enc2, ts: ts2 } = runComputePass(device,
|
|
83
85
|
pipelineReduce,
|
|
84
86
|
bgReduce,
|
|
85
87
|
1,
|
|
86
88
|
); // dispatch 1 workgroup to reduce the partial sums to a single result
|
|
87
|
-
readBuffer = stageReadback(enc2, resultBuffer);
|
|
89
|
+
readBuffer = stageReadback(device, enc2, resultBuffer);
|
|
88
90
|
|
|
89
|
-
submit(enc2);
|
|
91
|
+
submit(device, enc2);
|
|
90
92
|
|
|
91
93
|
const resultPromise = extractResult(readBuffer, Uint32Array);
|
|
92
94
|
readBuffer = null; // ownership transferred — extractResult's own finally destroys it
|
|
@@ -108,7 +110,7 @@ export async function isamax(device, n, x, incx) {
|
|
|
108
110
|
if (partialsIdxBuffer) destroyBuffers(partialsIdxBuffer);
|
|
109
111
|
if (resultBuffer) destroyBuffers(resultBuffer);
|
|
110
112
|
if (paramsBuffer) destroyBuffers(paramsBuffer);
|
|
111
|
-
// Only reached if submit(enc2) threw before ownership was transferred above.
|
|
113
|
+
// Only reached if submit(device, enc2) threw before ownership was transferred above.
|
|
112
114
|
if (readBuffer) destroyBuffers(readBuffer);
|
|
113
115
|
}
|
|
114
116
|
}
|
package/src/random/random.d.mts
CHANGED
|
@@ -83,6 +83,7 @@ export declare function randomFloat64Array(
|
|
|
83
83
|
*
|
|
84
84
|
* const n = 4, lda = n;
|
|
85
85
|
* const A = randomTriangularFloat32Array(n, lda, "lower");
|
|
86
|
+
* console.log(A);
|
|
86
87
|
* ```
|
|
87
88
|
* @see <a href="https://github.com/manit2004/wgblas/blob/main/src/random/random.mjs#L13">Source code: random.mjs (L13)</a>
|
|
88
89
|
* @category Utilities
|
package/src/sasum/sasum.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GpuVector } from "../classes/GpuVector.mjs";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Computes the sum of absolute values of a vector: result =
|
|
4
|
+
* Computes the sum of absolute values of a vector: $$\text{result} = \sum_{i} |x_i|$$
|
|
5
5
|
*
|
|
6
6
|
* {@includeCode ../../examples/sasum/sasum.js}
|
|
7
7
|
*
|
|
@@ -24,7 +24,7 @@ export declare function sasum(
|
|
|
24
24
|
): Promise<{ asum: number } | { asum: number; gpuTimeMs: number }>;
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Computes the sum of absolute values of a vector: result =
|
|
27
|
+
* Computes the sum of absolute values of a vector: $$\text{result} = \sum_{i} |x_i|$$
|
|
28
28
|
*
|
|
29
29
|
* {@includeCode ../../examples/sasum/gpu.sasum.js}
|
|
30
30
|
*
|
package/src/sasum/sasum.mjs
CHANGED
|
@@ -12,14 +12,16 @@ import { extractTimestamp } from "../util/benchmark.mjs";
|
|
|
12
12
|
import { extractResult } from "../util/result.mjs";
|
|
13
13
|
import { getPipeline } from "../util/pipeline.mjs";
|
|
14
14
|
import { GpuVector } from "../classes/GpuVector.mjs";
|
|
15
|
+
import { WGS } from "../util/constants.mjs";
|
|
16
|
+
import { requireSameDevice } from "../util/device.mjs";
|
|
15
17
|
|
|
16
|
-
const WGS = 64; // workgroup size
|
|
17
18
|
|
|
18
19
|
export async function sasum(device, n, x, incx) {
|
|
19
20
|
const xIsGpu = x instanceof GpuVector;
|
|
20
21
|
|
|
21
22
|
if (!(device instanceof GPUDevice))
|
|
22
23
|
throw new Error("device must be a GPUDevice.");
|
|
24
|
+
requireSameDevice(device, "sasum", { x });
|
|
23
25
|
if (!Number.isInteger(n) || !Number.isInteger(incx))
|
|
24
26
|
throw new Error("n and incx must be integers.");
|
|
25
27
|
if (incx <= 0) throw new Error("incx must be positive.");
|
|
@@ -41,10 +43,10 @@ export async function sasum(device, n, x, incx) {
|
|
|
41
43
|
let readBuffer = null;
|
|
42
44
|
|
|
43
45
|
try {
|
|
44
|
-
xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "sasum-x", false);
|
|
45
|
-
partialsBuffer = createStorageBuffer(2 * WGS * 4, "sasum-partials"); // 2*WGS partial sums of f32
|
|
46
|
-
resultBuffer = createResultBuffer(4, "sasum-result"); // final f32 scalar
|
|
47
|
-
paramsBuffer = createParamsBuffer(
|
|
46
|
+
xBuffer = xIsGpu ? x._buf : uploadBuffer(device, x, "sasum-x", false);
|
|
47
|
+
partialsBuffer = createStorageBuffer(device, 2 * WGS * 4, "sasum-partials"); // 2*WGS partial sums of f32
|
|
48
|
+
resultBuffer = createResultBuffer(device, 4, "sasum-result"); // final f32 scalar
|
|
49
|
+
paramsBuffer = createParamsBuffer(device,
|
|
48
50
|
[
|
|
49
51
|
{ value: n, type: "u32" },
|
|
50
52
|
{ value: incx, type: "u32" },
|
|
@@ -52,31 +54,31 @@ export async function sasum(device, n, x, incx) {
|
|
|
52
54
|
"sasum-params",
|
|
53
55
|
);
|
|
54
56
|
|
|
55
|
-
const bgMain = createBindGroup(pipelineMain.getBindGroupLayout(0), [
|
|
57
|
+
const bgMain = createBindGroup(device, pipelineMain.getBindGroupLayout(0), [
|
|
56
58
|
xBuffer,
|
|
57
59
|
partialsBuffer,
|
|
58
60
|
paramsBuffer,
|
|
59
61
|
]);
|
|
60
|
-
const { commandEncoder: enc1, ts: ts1 } = runComputePass(
|
|
62
|
+
const { commandEncoder: enc1, ts: ts1 } = runComputePass(device,
|
|
61
63
|
pipelineMain,
|
|
62
64
|
bgMain,
|
|
63
65
|
2 * WGS,
|
|
64
66
|
); // dispatch 2*WGS workgroups
|
|
65
67
|
|
|
66
|
-
submit(enc1);
|
|
68
|
+
submit(device, enc1);
|
|
67
69
|
|
|
68
|
-
const bgReduce = createBindGroup(pipelineReduce.getBindGroupLayout(0), [
|
|
70
|
+
const bgReduce = createBindGroup(device, pipelineReduce.getBindGroupLayout(0), [
|
|
69
71
|
partialsBuffer,
|
|
70
72
|
resultBuffer,
|
|
71
73
|
]);
|
|
72
|
-
const { commandEncoder: enc2, ts: ts2 } = runComputePass(
|
|
74
|
+
const { commandEncoder: enc2, ts: ts2 } = runComputePass(device,
|
|
73
75
|
pipelineReduce,
|
|
74
76
|
bgReduce,
|
|
75
77
|
1,
|
|
76
78
|
); // dispatch 1 workgroup to reduce the partial sums to a single result
|
|
77
|
-
readBuffer = stageReadback(enc2, resultBuffer);
|
|
79
|
+
readBuffer = stageReadback(device, enc2, resultBuffer);
|
|
78
80
|
|
|
79
|
-
submit(enc2);
|
|
81
|
+
submit(device, enc2);
|
|
80
82
|
|
|
81
83
|
const resultPromise = extractResult(readBuffer, Float32Array);
|
|
82
84
|
readBuffer = null; // ownership transferred — extractResult's own finally destroys it
|
|
@@ -96,7 +98,7 @@ export async function sasum(device, n, x, incx) {
|
|
|
96
98
|
if (partialsBuffer) destroyBuffers(partialsBuffer);
|
|
97
99
|
if (resultBuffer) destroyBuffers(resultBuffer);
|
|
98
100
|
if (paramsBuffer) destroyBuffers(paramsBuffer);
|
|
99
|
-
// Only reached if submit(enc2) threw before ownership was transferred above.
|
|
101
|
+
// Only reached if submit(device, enc2) threw before ownership was transferred above.
|
|
100
102
|
if (readBuffer) destroyBuffers(readBuffer);
|
|
101
103
|
}
|
|
102
104
|
}
|
package/src/saxpy/saxpy.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GpuVector } from "../classes/GpuVector.mjs";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Performs the operation y
|
|
4
|
+
* Performs the operation $$y \leftarrow \alpha x + y$$
|
|
5
5
|
*
|
|
6
6
|
* {@includeCode ../../examples/saxpy/saxpy.js}
|
|
7
7
|
*
|
|
@@ -29,7 +29,7 @@ export declare function saxpy(
|
|
|
29
29
|
): Promise<{ y: Float32Array } | { y: Float32Array; gpuTimeMs: number }>;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Performs the operation y
|
|
32
|
+
* Performs the operation $$y \leftarrow \alpha x + y$$
|
|
33
33
|
*
|
|
34
34
|
* {@includeCode ../../examples/saxpy/gpu.saxpy.js}
|
|
35
35
|
*
|
package/src/saxpy/saxpy.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { extractTimestamp } from "../util/benchmark.mjs";
|
|
|
11
11
|
import { getPipeline } from "../util/pipeline.mjs";
|
|
12
12
|
import { calcWorkgroups } from "../util/workgroup.mjs";
|
|
13
13
|
import { GpuVector } from "../classes/GpuVector.mjs";
|
|
14
|
+
import { requireSameDevice } from "../util/device.mjs";
|
|
14
15
|
|
|
15
16
|
export async function saxpy(device, n, alpha, x, incx, y, incy) {
|
|
16
17
|
const xIsGpu = x instanceof GpuVector;
|
|
@@ -18,6 +19,7 @@ export async function saxpy(device, n, alpha, x, incx, y, incy) {
|
|
|
18
19
|
|
|
19
20
|
if (!(device instanceof GPUDevice))
|
|
20
21
|
throw new Error("device must be a GPUDevice.");
|
|
22
|
+
requireSameDevice(device, "saxpy", { x, y });
|
|
21
23
|
if (
|
|
22
24
|
!Number.isInteger(n) ||
|
|
23
25
|
!Number.isInteger(incx) ||
|
|
@@ -56,9 +58,9 @@ export async function saxpy(device, n, alpha, x, incx, y, incy) {
|
|
|
56
58
|
let readBuffer = null;
|
|
57
59
|
|
|
58
60
|
try {
|
|
59
|
-
xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "saxpy-x", false);
|
|
60
|
-
yBuffer = yIsGpu ? y._buf : uploadBuffer(y, "saxpy-y", true);
|
|
61
|
-
paramsBuffer = createParamsBuffer(
|
|
61
|
+
xBuffer = xIsGpu ? x._buf : uploadBuffer(device, x, "saxpy-x", false);
|
|
62
|
+
yBuffer = yIsGpu ? y._buf : uploadBuffer(device, y, "saxpy-y", true);
|
|
63
|
+
paramsBuffer = createParamsBuffer(device,
|
|
62
64
|
[
|
|
63
65
|
{ value: n, type: "u32" },
|
|
64
66
|
{ value: alpha, type: "f32" },
|
|
@@ -68,19 +70,19 @@ export async function saxpy(device, n, alpha, x, incx, y, incy) {
|
|
|
68
70
|
"saxpy-params",
|
|
69
71
|
);
|
|
70
72
|
|
|
71
|
-
const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
|
|
73
|
+
const bindGroup = createBindGroup(device, pipeline.getBindGroupLayout(0), [
|
|
72
74
|
xBuffer,
|
|
73
75
|
yBuffer,
|
|
74
76
|
paramsBuffer,
|
|
75
77
|
]);
|
|
76
|
-
const { commandEncoder, ts } = runComputePass(
|
|
78
|
+
const { commandEncoder, ts } = runComputePass(device,
|
|
77
79
|
pipeline,
|
|
78
80
|
bindGroup,
|
|
79
|
-
calcWorkgroups(n),
|
|
81
|
+
calcWorkgroups(device, n),
|
|
80
82
|
);
|
|
81
|
-
readBuffer = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
|
|
83
|
+
readBuffer = yIsGpu ? null : stageReadback(device, commandEncoder, yBuffer);
|
|
82
84
|
|
|
83
|
-
submit(commandEncoder);
|
|
85
|
+
submit(device, commandEncoder);
|
|
84
86
|
|
|
85
87
|
const gpuTimeMs = await extractTimestamp(ts);
|
|
86
88
|
|
package/src/scopy/scopy.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GpuVector } from "../classes/GpuVector.mjs";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Performs the operation y
|
|
4
|
+
* Performs the operation $$y \leftarrow x$$
|
|
5
5
|
*
|
|
6
6
|
* {@includeCode ../../examples/scopy/scopy.js}
|
|
7
7
|
*
|
|
@@ -27,7 +27,7 @@ export declare function scopy(
|
|
|
27
27
|
): Promise<{ y: Float32Array } | { y: Float32Array; gpuTimeMs: number }>;
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* Performs the operation y
|
|
30
|
+
* Performs the operation $$y \leftarrow x$$
|
|
31
31
|
*
|
|
32
32
|
* {@includeCode ../../examples/scopy/gpu.scopy.js}
|
|
33
33
|
*
|
package/src/scopy/scopy.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { extractTimestamp } from "../util/benchmark.mjs";
|
|
|
11
11
|
import { getPipeline } from "../util/pipeline.mjs";
|
|
12
12
|
import { calcWorkgroups } from "../util/workgroup.mjs";
|
|
13
13
|
import { GpuVector } from "../classes/GpuVector.mjs";
|
|
14
|
+
import { requireSameDevice } from "../util/device.mjs";
|
|
14
15
|
|
|
15
16
|
export async function scopy(device, n, x, incx, y, incy) {
|
|
16
17
|
const xIsGpu = x instanceof GpuVector;
|
|
@@ -18,6 +19,7 @@ export async function scopy(device, n, x, incx, y, incy) {
|
|
|
18
19
|
|
|
19
20
|
if (!(device instanceof GPUDevice))
|
|
20
21
|
throw new Error("device must be a GPUDevice.");
|
|
22
|
+
requireSameDevice(device, "scopy", { x, y });
|
|
21
23
|
if (
|
|
22
24
|
!Number.isInteger(n) ||
|
|
23
25
|
!Number.isInteger(incx) ||
|
|
@@ -52,9 +54,9 @@ export async function scopy(device, n, x, incx, y, incy) {
|
|
|
52
54
|
let readBuffer = null;
|
|
53
55
|
|
|
54
56
|
try {
|
|
55
|
-
xBuffer = xIsGpu ? x._buf : uploadBuffer(x, "scopy-x", false);
|
|
56
|
-
yBuffer = yIsGpu ? y._buf : uploadBuffer(y, "scopy-y", true);
|
|
57
|
-
paramsBuffer = createParamsBuffer(
|
|
57
|
+
xBuffer = xIsGpu ? x._buf : uploadBuffer(device, x, "scopy-x", false);
|
|
58
|
+
yBuffer = yIsGpu ? y._buf : uploadBuffer(device, y, "scopy-y", true);
|
|
59
|
+
paramsBuffer = createParamsBuffer(device,
|
|
58
60
|
[
|
|
59
61
|
{ value: n, type: "u32" },
|
|
60
62
|
{ value: incx, type: "u32" },
|
|
@@ -63,19 +65,19 @@ export async function scopy(device, n, x, incx, y, incy) {
|
|
|
63
65
|
"scopy-params",
|
|
64
66
|
);
|
|
65
67
|
|
|
66
|
-
const bindGroup = createBindGroup(pipeline.getBindGroupLayout(0), [
|
|
68
|
+
const bindGroup = createBindGroup(device, pipeline.getBindGroupLayout(0), [
|
|
67
69
|
xBuffer,
|
|
68
70
|
yBuffer,
|
|
69
71
|
paramsBuffer,
|
|
70
72
|
]);
|
|
71
|
-
const { commandEncoder, ts } = runComputePass(
|
|
73
|
+
const { commandEncoder, ts } = runComputePass(device,
|
|
72
74
|
pipeline,
|
|
73
75
|
bindGroup,
|
|
74
|
-
calcWorkgroups(n),
|
|
76
|
+
calcWorkgroups(device, n),
|
|
75
77
|
);
|
|
76
|
-
readBuffer = yIsGpu ? null : stageReadback(commandEncoder, yBuffer);
|
|
78
|
+
readBuffer = yIsGpu ? null : stageReadback(device, commandEncoder, yBuffer);
|
|
77
79
|
|
|
78
|
-
submit(commandEncoder);
|
|
80
|
+
submit(device, commandEncoder);
|
|
79
81
|
|
|
80
82
|
const gpuTimeMs = await extractTimestamp(ts);
|
|
81
83
|
|
package/src/sdot/sdot.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GpuVector } from "../classes/GpuVector.mjs";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Computes the dot product of two vectors: result =
|
|
4
|
+
* Computes the dot product of two vectors: $$\text{result} = \sum_{i} x_i y_i$$
|
|
5
5
|
*
|
|
6
6
|
* {@includeCode ../../examples/sdot/sdot.js}
|
|
7
7
|
*
|
|
@@ -28,7 +28,7 @@ export declare function sdot(
|
|
|
28
28
|
): Promise<{ dot: number } | { dot: number; gpuTimeMs: number }>;
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* Computes the dot product of two vectors: result =
|
|
31
|
+
* Computes the dot product of two vectors: $$\text{result} = \sum_{i} x_i y_i$$
|
|
32
32
|
*
|
|
33
33
|
* {@includeCode ../../examples/sdot/gpu.sdot.js}
|
|
34
34
|
*
|