@danielsimonjr/mathts-parallel 0.6.10 → 0.7.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 +9 -3
- package/dist/ComputePool.d.ts +15 -14
- package/dist/ComputePool.d.ts.map +1 -1
- package/dist/WorkerPool.d.ts +55 -0
- package/dist/WorkerPool.d.ts.map +1 -0
- package/dist/gemm-rows.d.ts +19 -0
- package/dist/gemm-rows.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1159 -1752
- package/dist/matrix.worker.d.ts +0 -11
- package/dist/matrix.worker.d.ts.map +1 -1
- package/dist/matrix.worker.js +93 -110
- package/dist/strategies/chunk.d.ts +9 -2
- package/dist/strategies/chunk.d.ts.map +1 -1
- package/package.json +18 -7
package/dist/index.js
CHANGED
|
@@ -1,1885 +1,1292 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
MathWorkerPool,
|
|
11
|
-
Transfer
|
|
12
|
-
} from "@danielsimonjr/mathts-workerpool";
|
|
13
|
-
|
|
14
|
-
// src/strategies/chunk.ts
|
|
15
|
-
var DEFAULT_CHUNK_OPTIONS = {
|
|
16
|
-
minChunkSize: 1e3,
|
|
17
|
-
maxChunks: typeof navigator !== "undefined" ? navigator.hardwareConcurrency || 4 : 4,
|
|
18
|
-
targetChunkSize: 0,
|
|
19
|
-
// 0 = auto
|
|
20
|
-
balanced: true
|
|
1
|
+
import { norm2, pairwiseDot, pairwiseSum, scaledDistance, sumSquaredDeviations } from "@danielsimonjr/mathts-core";
|
|
2
|
+
import { MathWorkerPool, Transfer, tensordotRange } from "@danielsimonjr/mathts-workerpool";
|
|
3
|
+
//#region src/strategies/chunk.ts
|
|
4
|
+
const DEFAULT_CHUNK_OPTIONS = {
|
|
5
|
+
minChunkSize: 1e3,
|
|
6
|
+
maxChunks: typeof navigator !== "undefined" ? navigator.hardwareConcurrency || 4 : 4,
|
|
7
|
+
targetChunkSize: 0,
|
|
8
|
+
balanced: true
|
|
21
9
|
};
|
|
10
|
+
function planChunks(totalElements, options = {}, unbalancedChunkMin = 0) {
|
|
11
|
+
const { balanced } = {
|
|
12
|
+
...DEFAULT_CHUNK_OPTIONS,
|
|
13
|
+
...options
|
|
14
|
+
};
|
|
15
|
+
if (totalElements === 0) return [];
|
|
16
|
+
const numChunks = calculateOptimalChunks(totalElements, options);
|
|
17
|
+
if (numChunks <= 1) return [{
|
|
18
|
+
startIndex: 0,
|
|
19
|
+
endIndex: totalElements,
|
|
20
|
+
length: totalElements,
|
|
21
|
+
chunkIndex: 0
|
|
22
|
+
}];
|
|
23
|
+
const chunkInfo = [];
|
|
24
|
+
if (balanced) {
|
|
25
|
+
const baseChunkSize = Math.floor(totalElements / numChunks);
|
|
26
|
+
const remainder = totalElements % numChunks;
|
|
27
|
+
let currentIndex = 0;
|
|
28
|
+
for (let i = 0; i < numChunks; i++) {
|
|
29
|
+
const thisChunkSize = baseChunkSize + (i < remainder ? 1 : 0);
|
|
30
|
+
const endIndex = currentIndex + thisChunkSize;
|
|
31
|
+
chunkInfo.push({
|
|
32
|
+
startIndex: currentIndex,
|
|
33
|
+
endIndex,
|
|
34
|
+
length: thisChunkSize,
|
|
35
|
+
chunkIndex: i
|
|
36
|
+
});
|
|
37
|
+
currentIndex = endIndex;
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
const chunkSize = Math.max(unbalancedChunkMin, Math.ceil(totalElements / numChunks));
|
|
41
|
+
let currentIndex = 0;
|
|
42
|
+
let chunkIndex = 0;
|
|
43
|
+
while (currentIndex < totalElements) {
|
|
44
|
+
const endIndex = Math.min(currentIndex + chunkSize, totalElements);
|
|
45
|
+
const thisChunkSize = endIndex - currentIndex;
|
|
46
|
+
chunkInfo.push({
|
|
47
|
+
startIndex: currentIndex,
|
|
48
|
+
endIndex,
|
|
49
|
+
length: thisChunkSize,
|
|
50
|
+
chunkIndex
|
|
51
|
+
});
|
|
52
|
+
currentIndex = endIndex;
|
|
53
|
+
chunkIndex++;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return chunkInfo;
|
|
57
|
+
}
|
|
22
58
|
function calculateOptimalChunks(totalElements, options = {}) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
59
|
+
const { minChunkSize, maxChunks, targetChunkSize } = {
|
|
60
|
+
...DEFAULT_CHUNK_OPTIONS,
|
|
61
|
+
...options
|
|
62
|
+
};
|
|
63
|
+
if (totalElements === 0) return 0;
|
|
64
|
+
if (targetChunkSize > 0) return Math.min(maxChunks, Math.ceil(totalElements / targetChunkSize));
|
|
65
|
+
const maxPossibleChunks = Math.floor(totalElements / minChunkSize);
|
|
66
|
+
return Math.max(1, Math.min(maxChunks, maxPossibleChunks));
|
|
67
|
+
}
|
|
68
|
+
function assembleChunks(totalElements, options, minChunkSizeForPlan, whole, materialize) {
|
|
69
|
+
if (totalElements === 0) return {
|
|
70
|
+
chunks: [],
|
|
71
|
+
chunkInfo: [],
|
|
72
|
+
totalElements: 0,
|
|
73
|
+
numChunks: 0
|
|
74
|
+
};
|
|
75
|
+
const planned = calculateOptimalChunks(totalElements, options);
|
|
76
|
+
const chunkInfo = planChunks(totalElements, options, minChunkSizeForPlan);
|
|
77
|
+
if (planned <= 1) return {
|
|
78
|
+
chunks: [whole],
|
|
79
|
+
chunkInfo,
|
|
80
|
+
totalElements,
|
|
81
|
+
numChunks: 1
|
|
82
|
+
};
|
|
83
|
+
const chunks = chunkInfo.map((info) => materialize(info.startIndex, info.endIndex));
|
|
84
|
+
return {
|
|
85
|
+
chunks,
|
|
86
|
+
chunkInfo,
|
|
87
|
+
totalElements,
|
|
88
|
+
numChunks: chunks.length
|
|
89
|
+
};
|
|
33
90
|
}
|
|
34
91
|
function chunkFloat64Array(data, options = {}) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
chunkInfo: [],
|
|
41
|
-
totalElements: 0,
|
|
42
|
-
numChunks: 0
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
const numChunks = calculateOptimalChunks(totalElements, options);
|
|
46
|
-
if (numChunks <= 1) {
|
|
47
|
-
return {
|
|
48
|
-
chunks: [data],
|
|
49
|
-
chunkInfo: [
|
|
50
|
-
{
|
|
51
|
-
startIndex: 0,
|
|
52
|
-
endIndex: totalElements,
|
|
53
|
-
length: totalElements,
|
|
54
|
-
chunkIndex: 0
|
|
55
|
-
}
|
|
56
|
-
],
|
|
57
|
-
totalElements,
|
|
58
|
-
numChunks: 1
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
const chunks = [];
|
|
62
|
-
const chunkInfo = [];
|
|
63
|
-
if (balanced) {
|
|
64
|
-
const baseChunkSize = Math.floor(totalElements / numChunks);
|
|
65
|
-
const remainder = totalElements % numChunks;
|
|
66
|
-
let currentIndex = 0;
|
|
67
|
-
for (let i = 0; i < numChunks; i++) {
|
|
68
|
-
const thisChunkSize = baseChunkSize + (i < remainder ? 1 : 0);
|
|
69
|
-
const endIndex = currentIndex + thisChunkSize;
|
|
70
|
-
chunks.push(data.subarray(currentIndex, endIndex));
|
|
71
|
-
chunkInfo.push({
|
|
72
|
-
startIndex: currentIndex,
|
|
73
|
-
endIndex,
|
|
74
|
-
length: thisChunkSize,
|
|
75
|
-
chunkIndex: i
|
|
76
|
-
});
|
|
77
|
-
currentIndex = endIndex;
|
|
78
|
-
}
|
|
79
|
-
} else {
|
|
80
|
-
const chunkSize = Math.max(minChunkSize, Math.ceil(totalElements / numChunks));
|
|
81
|
-
let currentIndex = 0;
|
|
82
|
-
let chunkIndex = 0;
|
|
83
|
-
while (currentIndex < totalElements) {
|
|
84
|
-
const endIndex = Math.min(currentIndex + chunkSize, totalElements);
|
|
85
|
-
const thisChunkSize = endIndex - currentIndex;
|
|
86
|
-
chunks.push(data.subarray(currentIndex, endIndex));
|
|
87
|
-
chunkInfo.push({
|
|
88
|
-
startIndex: currentIndex,
|
|
89
|
-
endIndex,
|
|
90
|
-
length: thisChunkSize,
|
|
91
|
-
chunkIndex
|
|
92
|
-
});
|
|
93
|
-
currentIndex = endIndex;
|
|
94
|
-
chunkIndex++;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return {
|
|
98
|
-
chunks,
|
|
99
|
-
chunkInfo,
|
|
100
|
-
totalElements,
|
|
101
|
-
numChunks: chunks.length
|
|
102
|
-
};
|
|
92
|
+
const { minChunkSize } = {
|
|
93
|
+
...DEFAULT_CHUNK_OPTIONS,
|
|
94
|
+
...options
|
|
95
|
+
};
|
|
96
|
+
return assembleChunks(data.length, options, minChunkSize, data, (start, end) => data.subarray(start, end));
|
|
103
97
|
}
|
|
104
98
|
function chunkArray(data, options = {}) {
|
|
105
|
-
|
|
106
|
-
const totalElements = data.length;
|
|
107
|
-
if (totalElements === 0) {
|
|
108
|
-
return {
|
|
109
|
-
chunks: [],
|
|
110
|
-
chunkInfo: [],
|
|
111
|
-
totalElements: 0,
|
|
112
|
-
numChunks: 0
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
const numChunks = calculateOptimalChunks(totalElements, options);
|
|
116
|
-
if (numChunks <= 1) {
|
|
117
|
-
return {
|
|
118
|
-
chunks: [data],
|
|
119
|
-
chunkInfo: [
|
|
120
|
-
{
|
|
121
|
-
startIndex: 0,
|
|
122
|
-
endIndex: totalElements,
|
|
123
|
-
length: totalElements,
|
|
124
|
-
chunkIndex: 0
|
|
125
|
-
}
|
|
126
|
-
],
|
|
127
|
-
totalElements,
|
|
128
|
-
numChunks: 1
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
const chunks = [];
|
|
132
|
-
const chunkInfo = [];
|
|
133
|
-
if (balanced) {
|
|
134
|
-
const baseChunkSize = Math.floor(totalElements / numChunks);
|
|
135
|
-
const remainder = totalElements % numChunks;
|
|
136
|
-
let currentIndex = 0;
|
|
137
|
-
for (let i = 0; i < numChunks; i++) {
|
|
138
|
-
const thisChunkSize = baseChunkSize + (i < remainder ? 1 : 0);
|
|
139
|
-
const endIndex = currentIndex + thisChunkSize;
|
|
140
|
-
chunks.push(data.slice(currentIndex, endIndex));
|
|
141
|
-
chunkInfo.push({
|
|
142
|
-
startIndex: currentIndex,
|
|
143
|
-
endIndex,
|
|
144
|
-
length: thisChunkSize,
|
|
145
|
-
chunkIndex: i
|
|
146
|
-
});
|
|
147
|
-
currentIndex = endIndex;
|
|
148
|
-
}
|
|
149
|
-
} else {
|
|
150
|
-
const chunkSize = Math.ceil(totalElements / numChunks);
|
|
151
|
-
let currentIndex = 0;
|
|
152
|
-
let chunkIndex = 0;
|
|
153
|
-
while (currentIndex < totalElements) {
|
|
154
|
-
const endIndex = Math.min(currentIndex + chunkSize, totalElements);
|
|
155
|
-
const thisChunkSize = endIndex - currentIndex;
|
|
156
|
-
chunks.push(data.slice(currentIndex, endIndex));
|
|
157
|
-
chunkInfo.push({
|
|
158
|
-
startIndex: currentIndex,
|
|
159
|
-
endIndex,
|
|
160
|
-
length: thisChunkSize,
|
|
161
|
-
chunkIndex
|
|
162
|
-
});
|
|
163
|
-
currentIndex = endIndex;
|
|
164
|
-
chunkIndex++;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return {
|
|
168
|
-
chunks,
|
|
169
|
-
chunkInfo,
|
|
170
|
-
totalElements,
|
|
171
|
-
numChunks: chunks.length
|
|
172
|
-
};
|
|
99
|
+
return assembleChunks(data.length, options, 0, data, (start, end) => data.slice(start, end));
|
|
173
100
|
}
|
|
174
101
|
function mergeFloat64Chunks(chunks) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
102
|
+
if (chunks.length === 0) return /* @__PURE__ */ new Float64Array(0);
|
|
103
|
+
if (chunks.length === 1) return chunks[0];
|
|
104
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
105
|
+
const result = new Float64Array(totalLength);
|
|
106
|
+
let offset = 0;
|
|
107
|
+
for (const chunk of chunks) {
|
|
108
|
+
result.set(chunk, offset);
|
|
109
|
+
offset += chunk.length;
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
185
112
|
}
|
|
186
113
|
function mergeArrayChunks(chunks) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
114
|
+
if (chunks.length === 0) return [];
|
|
115
|
+
if (chunks.length === 1) return chunks[0];
|
|
116
|
+
return chunks.flat();
|
|
190
117
|
}
|
|
191
118
|
function shouldParallelize(elementCount, threshold = 1e4) {
|
|
192
|
-
|
|
119
|
+
return elementCount >= threshold;
|
|
193
120
|
}
|
|
194
121
|
function partitionRange(start, end, options = {}) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
return partitions;
|
|
122
|
+
const totalElements = end - start;
|
|
123
|
+
const numChunks = calculateOptimalChunks(totalElements, options);
|
|
124
|
+
if (numChunks <= 1) return [[start, end]];
|
|
125
|
+
const partitions = [];
|
|
126
|
+
const baseChunkSize = Math.floor(totalElements / numChunks);
|
|
127
|
+
const remainder = totalElements % numChunks;
|
|
128
|
+
let currentIndex = start;
|
|
129
|
+
for (let i = 0; i < numChunks; i++) {
|
|
130
|
+
const thisChunkSize = baseChunkSize + (i < remainder ? 1 : 0);
|
|
131
|
+
const endIndex = currentIndex + thisChunkSize;
|
|
132
|
+
partitions.push([currentIndex, endIndex]);
|
|
133
|
+
currentIndex = endIndex;
|
|
134
|
+
}
|
|
135
|
+
return partitions;
|
|
211
136
|
}
|
|
212
137
|
function partition2D(rows, cols, options = {}) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
138
|
+
return partitionRange(0, rows, options).map(([rowStart, rowEnd]) => [
|
|
139
|
+
rowStart,
|
|
140
|
+
rowEnd,
|
|
141
|
+
0,
|
|
142
|
+
cols
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/ops/bitwise.ts
|
|
218
147
|
function applyBinaryChunk(a, b, out, start, length, outStart, op) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
148
|
+
const end = start + length;
|
|
149
|
+
switch (op) {
|
|
150
|
+
case "bitAnd":
|
|
151
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] & b[i];
|
|
152
|
+
return;
|
|
153
|
+
case "bitOr":
|
|
154
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] | b[i];
|
|
155
|
+
return;
|
|
156
|
+
case "bitXor":
|
|
157
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] ^ b[i];
|
|
158
|
+
return;
|
|
159
|
+
case "leftShift":
|
|
160
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] << b[i];
|
|
161
|
+
return;
|
|
162
|
+
case "rightArithShift":
|
|
163
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] >> b[i];
|
|
164
|
+
return;
|
|
165
|
+
case "rightLogShift":
|
|
166
|
+
for (let i = start; i < end; i++) out[outStart + (i - start)] = a[i] >>> b[i];
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
240
169
|
}
|
|
241
170
|
function runBinary(a, b, op, options = {}) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
return out;
|
|
171
|
+
if (a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
172
|
+
const out = new Int32Array(a.length);
|
|
173
|
+
if (a.length === 0) return out;
|
|
174
|
+
const numChunks = Math.max(1, calculateOptimalChunks(a.length, options));
|
|
175
|
+
const base = Math.floor(a.length / numChunks);
|
|
176
|
+
const remainder = a.length % numChunks;
|
|
177
|
+
let start = 0;
|
|
178
|
+
for (let c = 0; c < numChunks; c++) {
|
|
179
|
+
const len = base + (c < remainder ? 1 : 0);
|
|
180
|
+
if (len === 0) continue;
|
|
181
|
+
applyBinaryChunk(a, b, out, start, len, start, op);
|
|
182
|
+
start += len;
|
|
183
|
+
}
|
|
184
|
+
return out;
|
|
258
185
|
}
|
|
259
186
|
function bitAnd(a, b, options) {
|
|
260
|
-
|
|
187
|
+
return runBinary(a, b, "bitAnd", options);
|
|
261
188
|
}
|
|
262
189
|
function bitOr(a, b, options) {
|
|
263
|
-
|
|
190
|
+
return runBinary(a, b, "bitOr", options);
|
|
264
191
|
}
|
|
265
192
|
function bitXor(a, b, options) {
|
|
266
|
-
|
|
193
|
+
return runBinary(a, b, "bitXor", options);
|
|
267
194
|
}
|
|
268
195
|
function leftShift(a, b, options) {
|
|
269
|
-
|
|
270
|
-
|
|
196
|
+
if (typeof b === "number") return runBinaryScalar(a, b, "leftShift", options);
|
|
197
|
+
return runBinary(a, b, "leftShift", options);
|
|
271
198
|
}
|
|
272
199
|
function rightArithShift(a, b, options) {
|
|
273
|
-
|
|
274
|
-
|
|
200
|
+
if (typeof b === "number") return runBinaryScalar(a, b, "rightArithShift", options);
|
|
201
|
+
return runBinary(a, b, "rightArithShift", options);
|
|
275
202
|
}
|
|
276
203
|
function rightLogShift(a, b, options) {
|
|
277
|
-
|
|
278
|
-
|
|
204
|
+
if (typeof b === "number") return runBinaryScalar(a, b, "rightLogShift", options);
|
|
205
|
+
return runBinary(a, b, "rightLogShift", options);
|
|
279
206
|
}
|
|
280
207
|
function bitNot(a, options = {}) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
208
|
+
const out = new Int32Array(a.length);
|
|
209
|
+
if (a.length === 0) return out;
|
|
210
|
+
const numChunks = Math.max(1, calculateOptimalChunks(a.length, options));
|
|
211
|
+
const base = Math.floor(a.length / numChunks);
|
|
212
|
+
const remainder = a.length % numChunks;
|
|
213
|
+
let start = 0;
|
|
214
|
+
for (let c = 0; c < numChunks; c++) {
|
|
215
|
+
const len = base + (c < remainder ? 1 : 0);
|
|
216
|
+
if (len === 0) continue;
|
|
217
|
+
const end = start + len;
|
|
218
|
+
for (let i = start; i < end; i++) out[i] = ~a[i];
|
|
219
|
+
start += len;
|
|
220
|
+
}
|
|
221
|
+
return out;
|
|
295
222
|
}
|
|
296
223
|
function runBinaryScalar(a, scalar, op, options = {}) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
return out;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// src/ComputePool.ts
|
|
224
|
+
const out = new Int32Array(a.length);
|
|
225
|
+
if (a.length === 0) return out;
|
|
226
|
+
const numChunks = Math.max(1, calculateOptimalChunks(a.length, options));
|
|
227
|
+
const base = Math.floor(a.length / numChunks);
|
|
228
|
+
const remainder = a.length % numChunks;
|
|
229
|
+
let start = 0;
|
|
230
|
+
for (let c = 0; c < numChunks; c++) {
|
|
231
|
+
const len = base + (c < remainder ? 1 : 0);
|
|
232
|
+
if (len === 0) continue;
|
|
233
|
+
const end = start + len;
|
|
234
|
+
switch (op) {
|
|
235
|
+
case "leftShift":
|
|
236
|
+
for (let i = start; i < end; i++) out[i] = a[i] << scalar;
|
|
237
|
+
break;
|
|
238
|
+
case "rightArithShift":
|
|
239
|
+
for (let i = start; i < end; i++) out[i] = a[i] >> scalar;
|
|
240
|
+
break;
|
|
241
|
+
case "rightLogShift":
|
|
242
|
+
for (let i = start; i < end; i++) out[i] = a[i] >>> scalar;
|
|
243
|
+
break;
|
|
244
|
+
case "bitAnd":
|
|
245
|
+
for (let i = start; i < end; i++) out[i] = a[i] & scalar;
|
|
246
|
+
break;
|
|
247
|
+
case "bitOr":
|
|
248
|
+
for (let i = start; i < end; i++) out[i] = a[i] | scalar;
|
|
249
|
+
break;
|
|
250
|
+
case "bitXor": for (let i = start; i < end; i++) out[i] = a[i] ^ scalar;
|
|
251
|
+
}
|
|
252
|
+
start += len;
|
|
253
|
+
}
|
|
254
|
+
return out;
|
|
255
|
+
}
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/ComputePool.ts
|
|
335
258
|
function resolveOpThreshold(t) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
// Added 2026-07-04 (WS-2 completion): the five OpNames that were absent from
|
|
399
|
-
// this map and silently rode the untested 50 000 global default. Benchmarked
|
|
400
|
-
// (tools/benchmarks/ws2-missing-ops.mjs, medians of 9 interleaved reps):
|
|
401
|
-
// histogram 0.23-0.56x, transpose 0.01-0.28x, matvec 0.00-0.05x, outer
|
|
402
|
-
// 0.01-0.50x at every size up to 4.2M elements - memory-bound / copy-dominated,
|
|
403
|
-
// no break-even -> 'never'. Their ComputePool methods previously dispatched to
|
|
404
|
-
// the worker pool UNCONDITIONALLY (no shouldParallelize gate); they now gate
|
|
405
|
-
// with an inline sequential fallback like the reductions above.
|
|
406
|
-
histogram: "never",
|
|
407
|
-
transpose: "never",
|
|
408
|
-
matvec: "never",
|
|
409
|
-
outer: "never",
|
|
410
|
-
// integrateFanOut loses ~50-100x for cheap integrands (0.01-0.02x at 128-32768
|
|
411
|
-
// evals). The fan-out is CALLER-OPT-IN (integration.ts dispatches only when the
|
|
412
|
-
// caller passes workerCount > 1), so this entry documents the default posture;
|
|
413
|
-
// no shouldParallelize call consults it. Expensive (ms-scale) integrands are the
|
|
414
|
-
// one case where the fan-out pays off.
|
|
415
|
-
integrateChunk: "never",
|
|
416
|
-
// Added 2026-07-05 (WS-2 addendum): the bitwise family gated via a NAMELESS
|
|
417
|
-
// shouldParallelize(len) -> the untested global 50k. Benchmarked
|
|
418
|
-
// (tools/benchmarks/ws2-bitwise-ops.mjs, medians of 9 interleaved reps):
|
|
419
|
-
// bitAnd 0.07-0.15x, bitNot 0.06-0.09x, leftShift 0.04-0.07x at every size up
|
|
420
|
-
// to 4M elements - Int32Array element-wise is maximally memory-bound; the
|
|
421
|
-
// worker path loses 7-25x. All seven -> 'never'; the methods now pass their
|
|
422
|
-
// op name so these entries actually govern.
|
|
423
|
-
bitAnd: "never",
|
|
424
|
-
bitOr: "never",
|
|
425
|
-
bitXor: "never",
|
|
426
|
-
bitNot: "never",
|
|
427
|
-
leftShift: "never",
|
|
428
|
-
rightArithShift: "never",
|
|
429
|
-
rightLogShift: "never",
|
|
430
|
-
// signal: overhead dominates or break-even never reached
|
|
431
|
-
parallelFFT: "never",
|
|
432
|
-
parallelConv: "never",
|
|
433
|
-
fft2d: "never",
|
|
434
|
-
// geometry: overhead dominates
|
|
435
|
-
distanceMatrix: "never",
|
|
436
|
-
// special functions: parallel wins above these sizes
|
|
437
|
-
erfc: 1e5,
|
|
438
|
-
besselJ: 1e6,
|
|
439
|
-
spectrogram: 65536,
|
|
440
|
-
// linear algebra: parallel wins above these element counts
|
|
441
|
-
matmul: 4096,
|
|
442
|
-
// break-even at 64×64 (4,096 elements)
|
|
443
|
-
matrixPower: 9216,
|
|
444
|
-
// break-even at ~96×96
|
|
445
|
-
characteristicPolynomial: 9216,
|
|
446
|
-
// break-even at ~96×96
|
|
447
|
-
// hypothesis tests: element-wise reduction (chiSquare) and post-sort
|
|
448
|
-
// dot-product (KS/MW/SW). Threshold 4096 per Slice 3.10 spec.
|
|
449
|
-
chiSquareTest: 4096,
|
|
450
|
-
kolmogorovSmirnovTest: 4096,
|
|
451
|
-
mannWhitneyTest: 4096,
|
|
452
|
-
shapiroWilkTest: 4096,
|
|
453
|
-
// distribution batch sampling: per-sample cost (rejection/transcendental) amortises
|
|
454
|
-
// worker overhead above 100 000 samples (Slice 5.12).
|
|
455
|
-
sampleChunk: 1e5
|
|
259
|
+
if (t === "never") return Number.MAX_SAFE_INTEGER;
|
|
260
|
+
if (t === "always") return 0;
|
|
261
|
+
return t;
|
|
262
|
+
}
|
|
263
|
+
const DEFAULT_THRESHOLD_BY_OP = {
|
|
264
|
+
add: "never",
|
|
265
|
+
subtract: "never",
|
|
266
|
+
multiply: "never",
|
|
267
|
+
divide: "never",
|
|
268
|
+
pow: "never",
|
|
269
|
+
scale: "never",
|
|
270
|
+
abs: "never",
|
|
271
|
+
negate: "never",
|
|
272
|
+
exp: "never",
|
|
273
|
+
log: "never",
|
|
274
|
+
sin: "never",
|
|
275
|
+
cos: "never",
|
|
276
|
+
tan: "never",
|
|
277
|
+
sign: "never",
|
|
278
|
+
sqrt: "never",
|
|
279
|
+
square: "never",
|
|
280
|
+
tensordot: 8192,
|
|
281
|
+
sum: "never",
|
|
282
|
+
mean: "never",
|
|
283
|
+
variance: "never",
|
|
284
|
+
parallelStatProd: "never",
|
|
285
|
+
normalCDF: "never",
|
|
286
|
+
norm: "never",
|
|
287
|
+
dot: "never",
|
|
288
|
+
min: "never",
|
|
289
|
+
max: "never",
|
|
290
|
+
minMax: "never",
|
|
291
|
+
std: "never",
|
|
292
|
+
prod: "never",
|
|
293
|
+
distance: "never",
|
|
294
|
+
histogram: "never",
|
|
295
|
+
transpose: "never",
|
|
296
|
+
matvec: "never",
|
|
297
|
+
outer: "never",
|
|
298
|
+
integrateChunk: "never",
|
|
299
|
+
bitAnd: "never",
|
|
300
|
+
bitOr: "never",
|
|
301
|
+
bitXor: "never",
|
|
302
|
+
bitNot: "never",
|
|
303
|
+
leftShift: "never",
|
|
304
|
+
rightArithShift: "never",
|
|
305
|
+
rightLogShift: "never",
|
|
306
|
+
parallelFFT: "never",
|
|
307
|
+
parallelConv: "never",
|
|
308
|
+
fft2d: "never",
|
|
309
|
+
distanceMatrix: "never",
|
|
310
|
+
erfc: 1e5,
|
|
311
|
+
besselJ: 1e6,
|
|
312
|
+
spectrogram: 65536,
|
|
313
|
+
matmul: 4096,
|
|
314
|
+
matrixPower: 9216,
|
|
315
|
+
characteristicPolynomial: 9216,
|
|
316
|
+
chiSquareTest: 4096,
|
|
317
|
+
kolmogorovSmirnovTest: 4096,
|
|
318
|
+
mannWhitneyTest: 4096,
|
|
319
|
+
shapiroWilkTest: 4096,
|
|
320
|
+
sampleChunk: 1e5
|
|
456
321
|
};
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
// 5 minutes
|
|
322
|
+
const DEFAULT_POOL_CONFIG = {
|
|
323
|
+
enabled: true,
|
|
324
|
+
minWorkers: 1,
|
|
325
|
+
maxWorkers: typeof navigator !== "undefined" ? navigator.hardwareConcurrency || 4 : 4,
|
|
326
|
+
thresholdElements: 5e4,
|
|
327
|
+
thresholdByOp: DEFAULT_THRESHOLD_BY_OP,
|
|
328
|
+
chunkSize: 1e4,
|
|
329
|
+
workerType: "auto",
|
|
330
|
+
workerIdleTimeout: 6e4,
|
|
331
|
+
taskTimeout: 3e5
|
|
468
332
|
};
|
|
469
333
|
function mergeThresholds(base, override) {
|
|
470
|
-
|
|
334
|
+
return override === void 0 ? base : {
|
|
335
|
+
...base,
|
|
336
|
+
...override
|
|
337
|
+
};
|
|
471
338
|
}
|
|
472
339
|
function toParallelResult(result) {
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
340
|
+
return {
|
|
341
|
+
result: result.result,
|
|
342
|
+
duration: result.duration,
|
|
343
|
+
chunks: result.chunks,
|
|
344
|
+
parallelized: result.parallelized
|
|
345
|
+
};
|
|
479
346
|
}
|
|
480
347
|
function toWorkerConfig(config) {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
}
|
|
492
|
-
function _rowMajorStrides(shape) {
|
|
493
|
-
const strides = new Array(shape.length);
|
|
494
|
-
let stride = 1;
|
|
495
|
-
for (let i = shape.length - 1; i >= 0; i--) {
|
|
496
|
-
strides[i] = stride;
|
|
497
|
-
stride *= shape[i];
|
|
498
|
-
}
|
|
499
|
-
return strides;
|
|
500
|
-
}
|
|
501
|
-
function _flatIndex(idx, strides) {
|
|
502
|
-
let flat = 0;
|
|
503
|
-
for (let i = 0; i < idx.length; i++) flat += idx[i] * strides[i];
|
|
504
|
-
return flat;
|
|
505
|
-
}
|
|
506
|
-
function _forEachIndex(sizes, cb) {
|
|
507
|
-
const rank = sizes.length;
|
|
508
|
-
if (rank === 0) {
|
|
509
|
-
cb([]);
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
const idx = new Array(rank).fill(0);
|
|
513
|
-
const total = sizes.reduce((a, b) => a * b, 1);
|
|
514
|
-
for (let n = 0; n < total; n++) {
|
|
515
|
-
cb(idx.slice());
|
|
516
|
-
for (let d = rank - 1; d >= 0; d--) {
|
|
517
|
-
idx[d]++;
|
|
518
|
-
if (idx[d] < sizes[d]) break;
|
|
519
|
-
idx[d] = 0;
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
function tensordotChunkKernel(a, aShape, b, bShape, axesA, axesB, resultShape, outStart, outEnd) {
|
|
524
|
-
const aStrides = _rowMajorStrides(aShape);
|
|
525
|
-
const bStrides = _rowMajorStrides(bShape);
|
|
526
|
-
const outStrides = _rowMajorStrides(resultShape);
|
|
527
|
-
const aRank = aShape.length;
|
|
528
|
-
const bRank = bShape.length;
|
|
529
|
-
const contractedAxesA = Array.from(axesA);
|
|
530
|
-
const contractedAxesB = Array.from(axesB);
|
|
531
|
-
const contractedSetA = new Set(contractedAxesA);
|
|
532
|
-
const contractedSetB = new Set(contractedAxesB);
|
|
533
|
-
const aFreeAxes = [];
|
|
534
|
-
const bFreeAxes = [];
|
|
535
|
-
for (let i = 0; i < aRank; i++) if (!contractedSetA.has(i)) aFreeAxes.push(i);
|
|
536
|
-
for (let i = 0; i < bRank; i++) if (!contractedSetB.has(i)) bFreeAxes.push(i);
|
|
537
|
-
const contractedSizes = contractedAxesA.map((ax) => aShape[ax]);
|
|
538
|
-
const chunkLen = outEnd - outStart;
|
|
539
|
-
const result = new Float64Array(chunkLen);
|
|
540
|
-
const outRank = resultShape.length;
|
|
541
|
-
const outIdx = new Array(outRank);
|
|
542
|
-
for (let outFlat = outStart; outFlat < outEnd; outFlat++) {
|
|
543
|
-
let rem = outFlat;
|
|
544
|
-
for (let d = 0; d < outRank; d++) {
|
|
545
|
-
outIdx[d] = Math.floor(rem / outStrides[d]);
|
|
546
|
-
rem -= outIdx[d] * outStrides[d];
|
|
547
|
-
}
|
|
548
|
-
const aFreeVals = outIdx.slice(0, aFreeAxes.length);
|
|
549
|
-
const bFreeVals = outIdx.slice(aFreeAxes.length);
|
|
550
|
-
let acc = 0;
|
|
551
|
-
_forEachIndex(contractedSizes, (contractVals) => {
|
|
552
|
-
const aIdx = new Array(aRank).fill(0);
|
|
553
|
-
for (let fi = 0; fi < aFreeAxes.length; fi++) aIdx[aFreeAxes[fi]] = aFreeVals[fi];
|
|
554
|
-
for (let ci = 0; ci < contractedAxesA.length; ci++)
|
|
555
|
-
aIdx[contractedAxesA[ci]] = contractVals[ci];
|
|
556
|
-
const bIdx = new Array(bRank).fill(0);
|
|
557
|
-
for (let fi = 0; fi < bFreeAxes.length; fi++) bIdx[bFreeAxes[fi]] = bFreeVals[fi];
|
|
558
|
-
for (let ci = 0; ci < contractedAxesB.length; ci++)
|
|
559
|
-
bIdx[contractedAxesB[ci]] = contractVals[ci];
|
|
560
|
-
acc += a[_flatIndex(aIdx, aStrides)] * b[_flatIndex(bIdx, bStrides)];
|
|
561
|
-
});
|
|
562
|
-
result[outFlat - outStart] = acc;
|
|
563
|
-
}
|
|
564
|
-
return result;
|
|
348
|
+
return {
|
|
349
|
+
enabled: config.enabled,
|
|
350
|
+
minWorkers: config.minWorkers,
|
|
351
|
+
maxWorkers: config.maxWorkers,
|
|
352
|
+
parallelThreshold: config.thresholdElements,
|
|
353
|
+
chunkSize: config.chunkSize,
|
|
354
|
+
workerType: config.workerType,
|
|
355
|
+
idleTimeout: config.workerIdleTimeout,
|
|
356
|
+
taskTimeout: config.taskTimeout
|
|
357
|
+
};
|
|
565
358
|
}
|
|
566
359
|
var ComputePool = class {
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
for (const buf of chunkResults) {
|
|
1253
|
-
const chunk = new Float64Array(buf);
|
|
1254
|
-
data2.set(chunk, offset);
|
|
1255
|
-
offset += chunk.length;
|
|
1256
|
-
}
|
|
1257
|
-
return {
|
|
1258
|
-
result: { data: data2, shape: resultShape },
|
|
1259
|
-
duration: performance.now() - start,
|
|
1260
|
-
chunks: chunks.length,
|
|
1261
|
-
parallelized: true
|
|
1262
|
-
};
|
|
1263
|
-
}
|
|
1264
|
-
const data = resultSize === 0 ? new Float64Array(0) : tensordotChunkKernel(a, aShape, b, bShape, axesA, axesB, resultShape, 0, resultSize);
|
|
1265
|
-
return {
|
|
1266
|
-
result: { data, shape: resultShape },
|
|
1267
|
-
duration: performance.now() - start,
|
|
1268
|
-
chunks: 1,
|
|
1269
|
-
parallelized: false
|
|
1270
|
-
};
|
|
1271
|
-
}
|
|
1272
|
-
/**
|
|
1273
|
-
* Compute mean in parallel (uses variance internally)
|
|
1274
|
-
*/
|
|
1275
|
-
async mean(data) {
|
|
1276
|
-
const result = await this.variance(data);
|
|
1277
|
-
return {
|
|
1278
|
-
...result,
|
|
1279
|
-
result: result.result.mean
|
|
1280
|
-
};
|
|
1281
|
-
}
|
|
1282
|
-
/**
|
|
1283
|
-
* Compute standard deviation in parallel
|
|
1284
|
-
*/
|
|
1285
|
-
async std(data) {
|
|
1286
|
-
const result = await this.variance(data);
|
|
1287
|
-
return {
|
|
1288
|
-
...result,
|
|
1289
|
-
result: result.result.std
|
|
1290
|
-
};
|
|
1291
|
-
}
|
|
1292
|
-
/**
|
|
1293
|
-
* Find minimum value in parallel
|
|
1294
|
-
*/
|
|
1295
|
-
async min(data) {
|
|
1296
|
-
const result = await this.minMax(data);
|
|
1297
|
-
return {
|
|
1298
|
-
...result,
|
|
1299
|
-
result: result.result.min
|
|
1300
|
-
};
|
|
1301
|
-
}
|
|
1302
|
-
/**
|
|
1303
|
-
* Find maximum value in parallel
|
|
1304
|
-
*/
|
|
1305
|
-
async max(data) {
|
|
1306
|
-
const result = await this.minMax(data);
|
|
1307
|
-
return {
|
|
1308
|
-
...result,
|
|
1309
|
-
result: result.result.max
|
|
1310
|
-
};
|
|
1311
|
-
}
|
|
1312
|
-
// =========================================================================
|
|
1313
|
-
// Bitwise Operations (Int32Array)
|
|
1314
|
-
// =========================================================================
|
|
1315
|
-
//
|
|
1316
|
-
// Above the elementwise threshold these dispatch to the `Int32Array`-keyed
|
|
1317
|
-
// worker kernels in `@danielsimonjr/mathts-workerpool` (`bitwiseChunk`,
|
|
1318
|
-
// `bitwiseScalarChunk`, `bitwiseNotChunk`). Below threshold they fall back
|
|
1319
|
-
// to the pure in-process drivers in `./ops/bitwise.ts`. The fallback path
|
|
1320
|
-
// uses those drivers verbatim so the algorithm lives in one place.
|
|
1321
|
-
/**
|
|
1322
|
-
* Element-wise bitwise AND on two `Int32Array`s.
|
|
1323
|
-
*/
|
|
1324
|
-
async bitAnd(a, b) {
|
|
1325
|
-
if (a.length !== b.length) {
|
|
1326
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1327
|
-
}
|
|
1328
|
-
if (this.shouldParallelize(a.length, "bitAnd")) {
|
|
1329
|
-
return toParallelResult(await this.workerPool.bitwiseBinary(a, b, "bitAnd"));
|
|
1330
|
-
}
|
|
1331
|
-
const start = performance.now();
|
|
1332
|
-
const result = bitAnd(a, b);
|
|
1333
|
-
return {
|
|
1334
|
-
result,
|
|
1335
|
-
duration: performance.now() - start,
|
|
1336
|
-
chunks: 1,
|
|
1337
|
-
parallelized: false
|
|
1338
|
-
};
|
|
1339
|
-
}
|
|
1340
|
-
/**
|
|
1341
|
-
* Element-wise bitwise OR on two `Int32Array`s.
|
|
1342
|
-
*/
|
|
1343
|
-
async bitOr(a, b) {
|
|
1344
|
-
if (a.length !== b.length) {
|
|
1345
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1346
|
-
}
|
|
1347
|
-
if (this.shouldParallelize(a.length, "bitOr")) {
|
|
1348
|
-
return toParallelResult(await this.workerPool.bitwiseBinary(a, b, "bitOr"));
|
|
1349
|
-
}
|
|
1350
|
-
const start = performance.now();
|
|
1351
|
-
const result = bitOr(a, b);
|
|
1352
|
-
return {
|
|
1353
|
-
result,
|
|
1354
|
-
duration: performance.now() - start,
|
|
1355
|
-
chunks: 1,
|
|
1356
|
-
parallelized: false
|
|
1357
|
-
};
|
|
1358
|
-
}
|
|
1359
|
-
/**
|
|
1360
|
-
* Element-wise bitwise XOR on two `Int32Array`s.
|
|
1361
|
-
*/
|
|
1362
|
-
async bitXor(a, b) {
|
|
1363
|
-
if (a.length !== b.length) {
|
|
1364
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1365
|
-
}
|
|
1366
|
-
if (this.shouldParallelize(a.length, "bitXor")) {
|
|
1367
|
-
return toParallelResult(await this.workerPool.bitwiseBinary(a, b, "bitXor"));
|
|
1368
|
-
}
|
|
1369
|
-
const start = performance.now();
|
|
1370
|
-
const result = bitXor(a, b);
|
|
1371
|
-
return {
|
|
1372
|
-
result,
|
|
1373
|
-
duration: performance.now() - start,
|
|
1374
|
-
chunks: 1,
|
|
1375
|
-
parallelized: false
|
|
1376
|
-
};
|
|
1377
|
-
}
|
|
1378
|
-
/**
|
|
1379
|
-
* Unary bitwise NOT on an `Int32Array`.
|
|
1380
|
-
*/
|
|
1381
|
-
async bitNot(a) {
|
|
1382
|
-
if (this.shouldParallelize(a.length, "bitNot")) {
|
|
1383
|
-
return toParallelResult(await this.workerPool.bitwiseNot(a));
|
|
1384
|
-
}
|
|
1385
|
-
const start = performance.now();
|
|
1386
|
-
const result = bitNot(a);
|
|
1387
|
-
return {
|
|
1388
|
-
result,
|
|
1389
|
-
duration: performance.now() - start,
|
|
1390
|
-
chunks: 1,
|
|
1391
|
-
parallelized: false
|
|
1392
|
-
};
|
|
1393
|
-
}
|
|
1394
|
-
/**
|
|
1395
|
-
* Element-wise left shift. `b` may be a per-element `Int32Array` of
|
|
1396
|
-
* shift counts or a single scalar `number` applied uniformly.
|
|
1397
|
-
*/
|
|
1398
|
-
async leftShift(a, b) {
|
|
1399
|
-
if (typeof b !== "number" && a.length !== b.length) {
|
|
1400
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1401
|
-
}
|
|
1402
|
-
if (this.shouldParallelize(a.length, "leftShift")) {
|
|
1403
|
-
const result2 = typeof b === "number" ? await this.workerPool.bitwiseScalar(a, b, "leftShift") : await this.workerPool.bitwiseBinary(a, b, "leftShift");
|
|
1404
|
-
return toParallelResult(result2);
|
|
1405
|
-
}
|
|
1406
|
-
const start = performance.now();
|
|
1407
|
-
const result = leftShift(a, b);
|
|
1408
|
-
return {
|
|
1409
|
-
result,
|
|
1410
|
-
duration: performance.now() - start,
|
|
1411
|
-
chunks: 1,
|
|
1412
|
-
parallelized: false
|
|
1413
|
-
};
|
|
1414
|
-
}
|
|
1415
|
-
/**
|
|
1416
|
-
* Element-wise arithmetic (sign-preserving) right shift.
|
|
1417
|
-
*/
|
|
1418
|
-
async rightArithShift(a, b) {
|
|
1419
|
-
if (typeof b !== "number" && a.length !== b.length) {
|
|
1420
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1421
|
-
}
|
|
1422
|
-
if (this.shouldParallelize(a.length, "rightArithShift")) {
|
|
1423
|
-
const result2 = typeof b === "number" ? await this.workerPool.bitwiseScalar(a, b, "rightArithShift") : await this.workerPool.bitwiseBinary(a, b, "rightArithShift");
|
|
1424
|
-
return toParallelResult(result2);
|
|
1425
|
-
}
|
|
1426
|
-
const start = performance.now();
|
|
1427
|
-
const result = rightArithShift(a, b);
|
|
1428
|
-
return {
|
|
1429
|
-
result,
|
|
1430
|
-
duration: performance.now() - start,
|
|
1431
|
-
chunks: 1,
|
|
1432
|
-
parallelized: false
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
/**
|
|
1436
|
-
* Element-wise logical (zero-filling) right shift. Results are stored
|
|
1437
|
-
* as `Int32Array`, so values ≥ 2^31 wrap into the negative range —
|
|
1438
|
-
* matching `(x >>> n) | 0` JavaScript semantics.
|
|
1439
|
-
*/
|
|
1440
|
-
async rightLogShift(a, b) {
|
|
1441
|
-
if (typeof b !== "number" && a.length !== b.length) {
|
|
1442
|
-
throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
1443
|
-
}
|
|
1444
|
-
if (this.shouldParallelize(a.length, "rightLogShift")) {
|
|
1445
|
-
const result2 = typeof b === "number" ? await this.workerPool.bitwiseScalar(a, b, "rightLogShift") : await this.workerPool.bitwiseBinary(a, b, "rightLogShift");
|
|
1446
|
-
return toParallelResult(result2);
|
|
1447
|
-
}
|
|
1448
|
-
const start = performance.now();
|
|
1449
|
-
const result = rightLogShift(a, b);
|
|
1450
|
-
return {
|
|
1451
|
-
result,
|
|
1452
|
-
duration: performance.now() - start,
|
|
1453
|
-
chunks: 1,
|
|
1454
|
-
parallelized: false
|
|
1455
|
-
};
|
|
1456
|
-
}
|
|
1457
|
-
/**
|
|
1458
|
-
* Terminate the worker pool
|
|
1459
|
-
*/
|
|
1460
|
-
async terminate(force = false) {
|
|
1461
|
-
await this.workerPool.terminate(force);
|
|
1462
|
-
}
|
|
1463
|
-
/**
|
|
1464
|
-
* Update configuration
|
|
1465
|
-
*/
|
|
1466
|
-
updateConfig(config) {
|
|
1467
|
-
this.config = {
|
|
1468
|
-
...this.config,
|
|
1469
|
-
...config,
|
|
1470
|
-
thresholdByOp: mergeThresholds(this.config.thresholdByOp, config.thresholdByOp)
|
|
1471
|
-
};
|
|
1472
|
-
this.workerPool.updateConfig(toWorkerConfig(this.config));
|
|
1473
|
-
}
|
|
1474
|
-
/**
|
|
1475
|
-
* Get current configuration
|
|
1476
|
-
*/
|
|
1477
|
-
getConfig() {
|
|
1478
|
-
return { ...this.config };
|
|
1479
|
-
}
|
|
1480
|
-
/**
|
|
1481
|
-
* Maximum number of workers configured for this pool.
|
|
1482
|
-
* Used by distribution sampling fan-out to determine chunk count.
|
|
1483
|
-
*/
|
|
1484
|
-
get workerCount() {
|
|
1485
|
-
return this.config.maxWorkers;
|
|
1486
|
-
}
|
|
1487
|
-
/**
|
|
1488
|
-
* Get the underlying MathWorkerPool for advanced operations
|
|
1489
|
-
*/
|
|
1490
|
-
getWorkerPool() {
|
|
1491
|
-
return this.workerPool;
|
|
1492
|
-
}
|
|
360
|
+
workerPool;
|
|
361
|
+
config;
|
|
362
|
+
constructor(config = {}) {
|
|
363
|
+
this.config = {
|
|
364
|
+
...DEFAULT_POOL_CONFIG,
|
|
365
|
+
...config,
|
|
366
|
+
thresholdByOp: mergeThresholds(DEFAULT_POOL_CONFIG.thresholdByOp, config.thresholdByOp)
|
|
367
|
+
};
|
|
368
|
+
this.workerPool = new MathWorkerPool(toWorkerConfig(this.config));
|
|
369
|
+
}
|
|
370
|
+
async initialize() {
|
|
371
|
+
await this.workerPool.initialize();
|
|
372
|
+
}
|
|
373
|
+
isReady() {
|
|
374
|
+
return this.workerPool.isReady();
|
|
375
|
+
}
|
|
376
|
+
shouldParallelize(elementCount, op) {
|
|
377
|
+
const byOp = this.config.thresholdByOp;
|
|
378
|
+
if (op !== void 0 && byOp !== void 0 && op in byOp) {
|
|
379
|
+
const raw = byOp[op];
|
|
380
|
+
if (raw !== void 0) {
|
|
381
|
+
const threshold = resolveOpThreshold(raw);
|
|
382
|
+
return this.config.enabled && elementCount >= threshold && this.workerPool.isReady();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return this.workerPool.shouldParallelize(elementCount);
|
|
386
|
+
}
|
|
387
|
+
seqResult(result) {
|
|
388
|
+
return {
|
|
389
|
+
result,
|
|
390
|
+
duration: 0,
|
|
391
|
+
chunks: 1,
|
|
392
|
+
parallelized: false
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
async gatedBinary(a, b, options) {
|
|
396
|
+
if (!this.shouldParallelize(a.length, options.op)) {
|
|
397
|
+
if (a.length !== b.length) throw new Error(`${options.op}: vectors must have the same length`);
|
|
398
|
+
return this.seqResult(options.sequential(a, b));
|
|
399
|
+
}
|
|
400
|
+
return toParallelResult(await options.worker(a, b));
|
|
401
|
+
}
|
|
402
|
+
async bitwiseBinary(a, b, options) {
|
|
403
|
+
if (a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
404
|
+
if (this.shouldParallelize(a.length, options.op)) return toParallelResult(await this.workerPool.bitwiseBinary(a, b, options.op));
|
|
405
|
+
const start = performance.now();
|
|
406
|
+
return {
|
|
407
|
+
result: options.fallback(a, b),
|
|
408
|
+
duration: performance.now() - start,
|
|
409
|
+
chunks: 1,
|
|
410
|
+
parallelized: false
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
async bitwiseShift(a, b, options) {
|
|
414
|
+
if (typeof b !== "number" && a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
415
|
+
if (this.shouldParallelize(a.length, options.op)) return toParallelResult(typeof b === "number" ? await this.workerPool.bitwiseScalar(a, b, options.op) : await this.workerPool.bitwiseBinary(a, b, options.op));
|
|
416
|
+
const start = performance.now();
|
|
417
|
+
return {
|
|
418
|
+
result: options.fallback(a, b),
|
|
419
|
+
duration: performance.now() - start,
|
|
420
|
+
chunks: 1,
|
|
421
|
+
parallelized: false
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
async exec(method, params, options) {
|
|
425
|
+
return this.workerPool.exec(method, params, options);
|
|
426
|
+
}
|
|
427
|
+
stats() {
|
|
428
|
+
return this.workerPool.stats();
|
|
429
|
+
}
|
|
430
|
+
async sum(data) {
|
|
431
|
+
if (!this.shouldParallelize(data.length, "sum")) return this.seqResult(pairwiseSum(data));
|
|
432
|
+
return toParallelResult(await this.workerPool.sum(data));
|
|
433
|
+
}
|
|
434
|
+
async prod(data) {
|
|
435
|
+
if (!this.shouldParallelize(data.length, "prod")) {
|
|
436
|
+
let p = 1;
|
|
437
|
+
for (let i = 0; i < data.length; i++) p *= data[i];
|
|
438
|
+
return this.seqResult(p);
|
|
439
|
+
}
|
|
440
|
+
return toParallelResult(await this.workerPool.prod(data));
|
|
441
|
+
}
|
|
442
|
+
async dot(a, b) {
|
|
443
|
+
return this.gatedBinary(a, b, {
|
|
444
|
+
op: "dot",
|
|
445
|
+
sequential: pairwiseDot,
|
|
446
|
+
worker: (x, y) => this.workerPool.dot(x, y)
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
poolOptions(elementCount, op) {
|
|
450
|
+
return this.shouldParallelize(elementCount, op) ? { forceParallel: true } : { forceSequential: true };
|
|
451
|
+
}
|
|
452
|
+
async elementwise(a, b, op) {
|
|
453
|
+
return toParallelResult(await this.workerPool.elementwise(a, b, op, this.poolOptions(a.length, op)));
|
|
454
|
+
}
|
|
455
|
+
async scale(data, scalar) {
|
|
456
|
+
return toParallelResult(await this.workerPool.scale(data, scalar, this.poolOptions(data.length, "scale")));
|
|
457
|
+
}
|
|
458
|
+
async matmul(a, aRows, aCols, b, bCols) {
|
|
459
|
+
return toParallelResult(await this.workerPool.matmul(a, aRows, aCols, b, bCols, this.poolOptions(aRows * bCols, "matmul")));
|
|
460
|
+
}
|
|
461
|
+
async transpose(data, rows, cols) {
|
|
462
|
+
if (!this.shouldParallelize(data.length, "transpose")) {
|
|
463
|
+
if (data.length !== rows * cols) throw new Error(`transpose: data length ${data.length} !== rows*cols (${rows * cols})`);
|
|
464
|
+
const out = new Float64Array(rows * cols);
|
|
465
|
+
for (let i = 0; i < rows; i++) for (let j = 0; j < cols; j++) out[j * rows + i] = data[i * cols + j];
|
|
466
|
+
return this.seqResult(out);
|
|
467
|
+
}
|
|
468
|
+
return toParallelResult(await this.workerPool.transpose(data, rows, cols));
|
|
469
|
+
}
|
|
470
|
+
async map(data, fn) {
|
|
471
|
+
return toParallelResult(await this.workerPool.map(data, fn));
|
|
472
|
+
}
|
|
473
|
+
async reduce(data, fn, initial) {
|
|
474
|
+
return toParallelResult(await this.workerPool.reduce(data, fn, initial));
|
|
475
|
+
}
|
|
476
|
+
async filter(data, predicate) {
|
|
477
|
+
return toParallelResult(await this.workerPool.filter(data, predicate));
|
|
478
|
+
}
|
|
479
|
+
async minMax(data) {
|
|
480
|
+
if (!this.shouldParallelize(data.length, "minMax") && !this.shouldParallelize(data.length, "min")) {
|
|
481
|
+
let min = Infinity;
|
|
482
|
+
let max = -Infinity;
|
|
483
|
+
let minIdx = -1;
|
|
484
|
+
let maxIdx = -1;
|
|
485
|
+
for (let i = 0; i < data.length; i++) {
|
|
486
|
+
if (data[i] < min) {
|
|
487
|
+
min = data[i];
|
|
488
|
+
minIdx = i;
|
|
489
|
+
}
|
|
490
|
+
if (data[i] > max) {
|
|
491
|
+
max = data[i];
|
|
492
|
+
maxIdx = i;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return this.seqResult({
|
|
496
|
+
min,
|
|
497
|
+
max,
|
|
498
|
+
minIdx,
|
|
499
|
+
maxIdx
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
return toParallelResult(await this.workerPool.minMax(data));
|
|
503
|
+
}
|
|
504
|
+
async variance(data) {
|
|
505
|
+
if (!this.shouldParallelize(data.length, "variance")) {
|
|
506
|
+
const n = data.length;
|
|
507
|
+
const mean = n > 0 ? pairwiseSum(data) / n : 0;
|
|
508
|
+
const variance = n > 0 ? sumSquaredDeviations(data) / n : 0;
|
|
509
|
+
return this.seqResult({
|
|
510
|
+
mean,
|
|
511
|
+
variance,
|
|
512
|
+
std: Math.sqrt(variance)
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
return toParallelResult(await this.workerPool.variance(data));
|
|
516
|
+
}
|
|
517
|
+
async norm(data) {
|
|
518
|
+
if (!this.shouldParallelize(data.length, "norm")) return this.seqResult(norm2(data));
|
|
519
|
+
return toParallelResult(await this.workerPool.norm(data));
|
|
520
|
+
}
|
|
521
|
+
async distance(a, b) {
|
|
522
|
+
return this.gatedBinary(a, b, {
|
|
523
|
+
op: "distance",
|
|
524
|
+
sequential: scaledDistance,
|
|
525
|
+
worker: (x, y) => this.workerPool.distance(x, y)
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
async histogram(data, bins, min, max) {
|
|
529
|
+
if (!this.shouldParallelize(data.length, "histogram")) {
|
|
530
|
+
let lo = min;
|
|
531
|
+
let hi = max;
|
|
532
|
+
if (lo === void 0 || hi === void 0) {
|
|
533
|
+
lo = Infinity;
|
|
534
|
+
hi = -Infinity;
|
|
535
|
+
for (let i = 0; i < data.length; i++) {
|
|
536
|
+
if (data[i] < lo) lo = data[i];
|
|
537
|
+
if (data[i] > hi) hi = data[i];
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
const counts = new Array(bins).fill(0);
|
|
541
|
+
const width = (hi - lo) / bins || 1;
|
|
542
|
+
for (let i = 0; i < data.length; i++) {
|
|
543
|
+
let b = Math.floor((data[i] - lo) / width);
|
|
544
|
+
if (b >= bins) b = bins - 1;
|
|
545
|
+
if (b < 0) b = 0;
|
|
546
|
+
counts[b]++;
|
|
547
|
+
}
|
|
548
|
+
return this.seqResult(counts);
|
|
549
|
+
}
|
|
550
|
+
return toParallelResult(await this.workerPool.histogram(data, bins, min, max));
|
|
551
|
+
}
|
|
552
|
+
async unary(data, fn) {
|
|
553
|
+
return toParallelResult(await this.workerPool.unary(data, fn, this.poolOptions(data.length, fn)));
|
|
554
|
+
}
|
|
555
|
+
async applyKernel(data, fnSource) {
|
|
556
|
+
return toParallelResult(await this.workerPool.applyKernel(data, fnSource));
|
|
557
|
+
}
|
|
558
|
+
async applyKernel2(a, b, fnSource) {
|
|
559
|
+
return toParallelResult(await this.workerPool.applyKernel2(a, b, fnSource));
|
|
560
|
+
}
|
|
561
|
+
async integrateFanOut(fnSource, a, b, workerCount, nodes, weights) {
|
|
562
|
+
const h = (b - a) / workerCount;
|
|
563
|
+
const tasks = [];
|
|
564
|
+
for (let k = 0; k < workerCount; k++) {
|
|
565
|
+
const subA = a + k * h;
|
|
566
|
+
const subB = a + (k + 1) * h;
|
|
567
|
+
tasks.push(this.workerPool.exec("integrateChunk", [
|
|
568
|
+
fnSource,
|
|
569
|
+
subA,
|
|
570
|
+
subB,
|
|
571
|
+
nodes,
|
|
572
|
+
weights
|
|
573
|
+
]));
|
|
574
|
+
}
|
|
575
|
+
return (await Promise.all(tasks)).reduce((acc, v) => acc + v, 0);
|
|
576
|
+
}
|
|
577
|
+
async distributionSampleFanOut(distName, params, n, baseSeed, workerCount) {
|
|
578
|
+
const SPLIT_MIX_CONST = 2654435769;
|
|
579
|
+
const baseChunkSize = Math.floor(n / workerCount);
|
|
580
|
+
const tasks = [];
|
|
581
|
+
for (let k = 0; k < workerCount; k++) {
|
|
582
|
+
const chunkCount = k < workerCount - 1 ? baseChunkSize : n - baseChunkSize * (workerCount - 1);
|
|
583
|
+
const chunkSeed = (baseSeed ^ (Math.imul(k, SPLIT_MIX_CONST) | 0)) >>> 0;
|
|
584
|
+
tasks.push(this.workerPool.exec("sampleChunk", [
|
|
585
|
+
distName,
|
|
586
|
+
params,
|
|
587
|
+
chunkSeed,
|
|
588
|
+
chunkCount
|
|
589
|
+
]));
|
|
590
|
+
}
|
|
591
|
+
const buffers = await Promise.all(tasks);
|
|
592
|
+
const out = new Float64Array(n);
|
|
593
|
+
let offset = 0;
|
|
594
|
+
for (const buf of buffers) {
|
|
595
|
+
const chunk = new Float64Array(buf);
|
|
596
|
+
out.set(chunk, offset);
|
|
597
|
+
offset += chunk.length;
|
|
598
|
+
}
|
|
599
|
+
return out;
|
|
600
|
+
}
|
|
601
|
+
async fftBatch(real, imag, frameCount, frameLength, inverse = false) {
|
|
602
|
+
return toParallelResult(await this.workerPool.fftBatch(real, imag, frameCount, frameLength, inverse));
|
|
603
|
+
}
|
|
604
|
+
async distanceMatrix(points, n, dim) {
|
|
605
|
+
return toParallelResult(await this.workerPool.distanceMatrix(points, n, dim));
|
|
606
|
+
}
|
|
607
|
+
async abs(data) {
|
|
608
|
+
return this.unary(data, "abs");
|
|
609
|
+
}
|
|
610
|
+
async sqrt(data) {
|
|
611
|
+
return this.unary(data, "sqrt");
|
|
612
|
+
}
|
|
613
|
+
async exp(data) {
|
|
614
|
+
return this.unary(data, "exp");
|
|
615
|
+
}
|
|
616
|
+
async log(data) {
|
|
617
|
+
return this.unary(data, "log");
|
|
618
|
+
}
|
|
619
|
+
async sin(data) {
|
|
620
|
+
return this.unary(data, "sin");
|
|
621
|
+
}
|
|
622
|
+
async cos(data) {
|
|
623
|
+
return this.unary(data, "cos");
|
|
624
|
+
}
|
|
625
|
+
async tan(data) {
|
|
626
|
+
return this.unary(data, "tan");
|
|
627
|
+
}
|
|
628
|
+
async negate(data) {
|
|
629
|
+
return this.unary(data, "negate");
|
|
630
|
+
}
|
|
631
|
+
async square(data) {
|
|
632
|
+
return this.unary(data, "square");
|
|
633
|
+
}
|
|
634
|
+
async matvec(matrix, rows, cols, vector) {
|
|
635
|
+
if (!this.shouldParallelize(matrix.length, "matvec")) {
|
|
636
|
+
if (matrix.length !== rows * cols) throw new Error(`matvec: matrix length ${matrix.length} !== rows*cols (${rows * cols})`);
|
|
637
|
+
if (vector.length !== cols) throw new Error(`matvec: vector length ${vector.length} !== cols (${cols})`);
|
|
638
|
+
const out = new Float64Array(rows);
|
|
639
|
+
for (let i = 0; i < rows; i++) {
|
|
640
|
+
let s = 0;
|
|
641
|
+
for (let j = 0; j < cols; j++) s += matrix[i * cols + j] * vector[j];
|
|
642
|
+
out[i] = s;
|
|
643
|
+
}
|
|
644
|
+
return this.seqResult(out);
|
|
645
|
+
}
|
|
646
|
+
return toParallelResult(await this.workerPool.matvec(matrix, rows, cols, vector));
|
|
647
|
+
}
|
|
648
|
+
async outer(a, b) {
|
|
649
|
+
if (!this.shouldParallelize(a.length * b.length, "outer")) {
|
|
650
|
+
const out = new Float64Array(a.length * b.length);
|
|
651
|
+
for (let i = 0; i < a.length; i++) for (let j = 0; j < b.length; j++) out[i * b.length + j] = a[i] * b[j];
|
|
652
|
+
return this.seqResult(out);
|
|
653
|
+
}
|
|
654
|
+
return toParallelResult(await this.workerPool.outer(a, b));
|
|
655
|
+
}
|
|
656
|
+
async find(data, predicate) {
|
|
657
|
+
return toParallelResult(await this.workerPool.find(data, predicate));
|
|
658
|
+
}
|
|
659
|
+
async sort(data, compare) {
|
|
660
|
+
return toParallelResult(await this.workerPool.sort(data, compare));
|
|
661
|
+
}
|
|
662
|
+
async add(a, b) {
|
|
663
|
+
return this.elementwise(a, b, "add");
|
|
664
|
+
}
|
|
665
|
+
async subtract(a, b) {
|
|
666
|
+
return this.elementwise(a, b, "subtract");
|
|
667
|
+
}
|
|
668
|
+
async multiply(a, b) {
|
|
669
|
+
return this.elementwise(a, b, "multiply");
|
|
670
|
+
}
|
|
671
|
+
async divide(a, b) {
|
|
672
|
+
return this.elementwise(a, b, "divide");
|
|
673
|
+
}
|
|
674
|
+
async pow(a, b) {
|
|
675
|
+
if (a.length !== b.length) throw new Error(`Array lengths must match: ${a.length} vs ${b.length}`);
|
|
676
|
+
if (this.shouldParallelize(a.length, "pow")) return this.applyKernel2(a, b, "(a, b) => Math.pow(a, b)");
|
|
677
|
+
const start = performance.now();
|
|
678
|
+
const result = new Float64Array(a.length);
|
|
679
|
+
for (let i = 0; i < a.length; i++) result[i] = a[i] ** b[i];
|
|
680
|
+
return {
|
|
681
|
+
result,
|
|
682
|
+
duration: performance.now() - start,
|
|
683
|
+
chunks: 1,
|
|
684
|
+
parallelized: false
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
async sign(data) {
|
|
688
|
+
if (this.shouldParallelize(data.length, "sign")) return this.applyKernel(data, "(x) => Math.sign(x)");
|
|
689
|
+
const start = performance.now();
|
|
690
|
+
const result = new Float64Array(data.length);
|
|
691
|
+
for (let i = 0; i < data.length; i++) result[i] = Math.sign(data[i]);
|
|
692
|
+
return {
|
|
693
|
+
result,
|
|
694
|
+
duration: performance.now() - start,
|
|
695
|
+
chunks: 1,
|
|
696
|
+
parallelized: false
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
async tensordot(a, aShape, b, bShape, axesA, axesB) {
|
|
700
|
+
if (axesA.length !== axesB.length) throw new Error(`tensordot: axesA.length (${axesA.length}) !== axesB.length (${axesB.length})`);
|
|
701
|
+
const aRank = aShape.length;
|
|
702
|
+
const bRank = bShape.length;
|
|
703
|
+
const aContracted = /* @__PURE__ */ new Set();
|
|
704
|
+
const bContracted = /* @__PURE__ */ new Set();
|
|
705
|
+
for (let i = 0; i < axesA.length; i++) {
|
|
706
|
+
const ax = axesA[i];
|
|
707
|
+
const bx = axesB[i];
|
|
708
|
+
if (!Number.isInteger(ax) || ax < 0 || ax >= aRank) throw new Error(`tensordot: axesA[${i}]=${ax} is out of range for aShape (rank ${aRank})`);
|
|
709
|
+
if (!Number.isInteger(bx) || bx < 0 || bx >= bRank) throw new Error(`tensordot: axesB[${i}]=${bx} is out of range for bShape (rank ${bRank})`);
|
|
710
|
+
if (aShape[ax] !== bShape[bx]) throw new Error(`tensordot: dimension mismatch on contracted axes: aShape[${ax}]=${aShape[ax]} vs bShape[${bx}]=${bShape[bx]}`);
|
|
711
|
+
aContracted.add(ax);
|
|
712
|
+
bContracted.add(bx);
|
|
713
|
+
}
|
|
714
|
+
const resultShape = [];
|
|
715
|
+
for (let i = 0; i < aRank; i++) if (!aContracted.has(i)) resultShape.push(aShape[i]);
|
|
716
|
+
for (let i = 0; i < bRank; i++) if (!bContracted.has(i)) resultShape.push(bShape[i]);
|
|
717
|
+
const resultSize = resultShape.reduce((acc, d) => acc * d, 1);
|
|
718
|
+
const contractedVolume = Array.from(axesA).reduce((acc, ax) => acc * aShape[ax], 1);
|
|
719
|
+
const start = performance.now();
|
|
720
|
+
if (this.shouldParallelize(contractedVolume, "tensordot") && resultSize > 0) {
|
|
721
|
+
const chunkSize = this.config.chunkSize;
|
|
722
|
+
const chunks = [];
|
|
723
|
+
for (let i = 0; i < resultSize; i += chunkSize) chunks.push([i, Math.min(i + chunkSize, resultSize)]);
|
|
724
|
+
const aShapeArr = Array.from(aShape);
|
|
725
|
+
const bShapeArr = Array.from(bShape);
|
|
726
|
+
const axesAArr = Array.from(axesA);
|
|
727
|
+
const axesBArr = Array.from(axesB);
|
|
728
|
+
const chunkResults = await Promise.all(chunks.map(([outStart, outEnd]) => this.workerPool.exec("tensordotChunk", [
|
|
729
|
+
a.buffer,
|
|
730
|
+
aShapeArr,
|
|
731
|
+
b.buffer,
|
|
732
|
+
bShapeArr,
|
|
733
|
+
axesAArr,
|
|
734
|
+
axesBArr,
|
|
735
|
+
resultShape,
|
|
736
|
+
outStart,
|
|
737
|
+
outEnd
|
|
738
|
+
])));
|
|
739
|
+
const data = new Float64Array(resultSize);
|
|
740
|
+
let offset = 0;
|
|
741
|
+
for (const buf of chunkResults) {
|
|
742
|
+
const chunk = new Float64Array(buf);
|
|
743
|
+
data.set(chunk, offset);
|
|
744
|
+
offset += chunk.length;
|
|
745
|
+
}
|
|
746
|
+
return {
|
|
747
|
+
result: {
|
|
748
|
+
data,
|
|
749
|
+
shape: resultShape
|
|
750
|
+
},
|
|
751
|
+
duration: performance.now() - start,
|
|
752
|
+
chunks: chunks.length,
|
|
753
|
+
parallelized: true
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
return {
|
|
757
|
+
result: {
|
|
758
|
+
data: resultSize === 0 ? /* @__PURE__ */ new Float64Array(0) : tensordotRange(a, aShape, b, bShape, axesA, axesB, resultShape, 0, resultSize),
|
|
759
|
+
shape: resultShape
|
|
760
|
+
},
|
|
761
|
+
duration: performance.now() - start,
|
|
762
|
+
chunks: 1,
|
|
763
|
+
parallelized: false
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
async mean(data) {
|
|
767
|
+
const result = await this.variance(data);
|
|
768
|
+
return {
|
|
769
|
+
...result,
|
|
770
|
+
result: result.result.mean
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
async std(data) {
|
|
774
|
+
const result = await this.variance(data);
|
|
775
|
+
return {
|
|
776
|
+
...result,
|
|
777
|
+
result: result.result.std
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
async min(data) {
|
|
781
|
+
const result = await this.minMax(data);
|
|
782
|
+
return {
|
|
783
|
+
...result,
|
|
784
|
+
result: result.result.min
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
async max(data) {
|
|
788
|
+
const result = await this.minMax(data);
|
|
789
|
+
return {
|
|
790
|
+
...result,
|
|
791
|
+
result: result.result.max
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
async bitAnd(a, b) {
|
|
795
|
+
return this.bitwiseBinary(a, b, {
|
|
796
|
+
op: "bitAnd",
|
|
797
|
+
fallback: bitAnd
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
async bitOr(a, b) {
|
|
801
|
+
return this.bitwiseBinary(a, b, {
|
|
802
|
+
op: "bitOr",
|
|
803
|
+
fallback: bitOr
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
async bitXor(a, b) {
|
|
807
|
+
return this.bitwiseBinary(a, b, {
|
|
808
|
+
op: "bitXor",
|
|
809
|
+
fallback: bitXor
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
async bitNot(a) {
|
|
813
|
+
if (this.shouldParallelize(a.length, "bitNot")) return toParallelResult(await this.workerPool.bitwiseNot(a));
|
|
814
|
+
const start = performance.now();
|
|
815
|
+
return {
|
|
816
|
+
result: bitNot(a),
|
|
817
|
+
duration: performance.now() - start,
|
|
818
|
+
chunks: 1,
|
|
819
|
+
parallelized: false
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
async leftShift(a, b) {
|
|
823
|
+
return this.bitwiseShift(a, b, {
|
|
824
|
+
op: "leftShift",
|
|
825
|
+
fallback: leftShift
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
async rightArithShift(a, b) {
|
|
829
|
+
return this.bitwiseShift(a, b, {
|
|
830
|
+
op: "rightArithShift",
|
|
831
|
+
fallback: rightArithShift
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
async rightLogShift(a, b) {
|
|
835
|
+
return this.bitwiseShift(a, b, {
|
|
836
|
+
op: "rightLogShift",
|
|
837
|
+
fallback: rightLogShift
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
async terminate(force = false) {
|
|
841
|
+
await this.workerPool.terminate(force);
|
|
842
|
+
}
|
|
843
|
+
updateConfig(config) {
|
|
844
|
+
this.config = {
|
|
845
|
+
...this.config,
|
|
846
|
+
...config,
|
|
847
|
+
thresholdByOp: mergeThresholds(this.config.thresholdByOp, config.thresholdByOp)
|
|
848
|
+
};
|
|
849
|
+
this.workerPool.updateConfig(toWorkerConfig(this.config));
|
|
850
|
+
}
|
|
851
|
+
getConfig() {
|
|
852
|
+
return { ...this.config };
|
|
853
|
+
}
|
|
854
|
+
get workerCount() {
|
|
855
|
+
return this.config.maxWorkers;
|
|
856
|
+
}
|
|
857
|
+
getWorkerPool() {
|
|
858
|
+
return this.workerPool;
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
const computePool = new ComputePool();
|
|
862
|
+
//#endregion
|
|
863
|
+
//#region src/WorkerPool.ts
|
|
864
|
+
var WorkerPool = class {
|
|
865
|
+
workers = [];
|
|
866
|
+
availableWorkers = [];
|
|
867
|
+
taskQueue = [];
|
|
868
|
+
activeTasks = /* @__PURE__ */ new Map();
|
|
869
|
+
maxWorkers;
|
|
870
|
+
workerScript;
|
|
871
|
+
isNode;
|
|
872
|
+
terminated = false;
|
|
873
|
+
constructor(workerScript, maxWorkers) {
|
|
874
|
+
this.workerScript = workerScript;
|
|
875
|
+
this.maxWorkers = maxWorkers || this.getOptimalWorkerCount();
|
|
876
|
+
this.isNode = typeof process !== "undefined" && process.versions?.node !== void 0;
|
|
877
|
+
this.initialize();
|
|
878
|
+
}
|
|
879
|
+
getOptimalWorkerCount() {
|
|
880
|
+
if (typeof navigator !== "undefined" && navigator.hardwareConcurrency) return Math.max(2, navigator.hardwareConcurrency - 1);
|
|
881
|
+
return 4;
|
|
882
|
+
}
|
|
883
|
+
async initialize() {
|
|
884
|
+
for (let i = 0; i < this.maxWorkers; i++) {
|
|
885
|
+
const worker = await this.createWorker();
|
|
886
|
+
if (this.terminated) {
|
|
887
|
+
worker.terminate();
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
this.workers.push(worker);
|
|
891
|
+
this.availableWorkers.push(worker);
|
|
892
|
+
this.processQueue();
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
async createWorker() {
|
|
896
|
+
let worker;
|
|
897
|
+
if (this.isNode) {
|
|
898
|
+
const { Worker: NodeWorker } = await import("worker_threads");
|
|
899
|
+
const nodeWorker = new NodeWorker(this.workerScript.startsWith("file://") ? new URL(this.workerScript) : this.workerScript);
|
|
900
|
+
nodeWorker.on("message", (data) => {
|
|
901
|
+
this.handleWorkerMessage(worker, data);
|
|
902
|
+
});
|
|
903
|
+
nodeWorker.on("error", (error) => {
|
|
904
|
+
this.handleWorkerError(worker, { message: error.message });
|
|
905
|
+
});
|
|
906
|
+
worker = nodeWorker;
|
|
907
|
+
} else {
|
|
908
|
+
worker = new Worker(this.workerScript, { type: "module" });
|
|
909
|
+
worker.onmessage = (event) => {
|
|
910
|
+
this.handleWorkerMessage(worker, event.data);
|
|
911
|
+
};
|
|
912
|
+
worker.onerror = (error) => {
|
|
913
|
+
this.handleWorkerError(worker, error);
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
return worker;
|
|
917
|
+
}
|
|
918
|
+
handleWorkerMessage(worker, message) {
|
|
919
|
+
const task = this.activeTasks.get(message.id);
|
|
920
|
+
if (!task) return;
|
|
921
|
+
this.activeTasks.delete(message.id);
|
|
922
|
+
if (task.timeoutHandle) {
|
|
923
|
+
clearTimeout(task.timeoutHandle);
|
|
924
|
+
task.timeoutHandle = void 0;
|
|
925
|
+
}
|
|
926
|
+
if (message.type === "result") task.resolve(message.data);
|
|
927
|
+
else if (message.type === "error") task.reject(new Error(message.error || "Worker error"));
|
|
928
|
+
this.availableWorkers.push(worker);
|
|
929
|
+
this.processQueue();
|
|
930
|
+
}
|
|
931
|
+
handleWorkerError(worker, error) {
|
|
932
|
+
console.error("Worker error:", error);
|
|
933
|
+
for (const [id, task] of this.activeTasks) if (this.workers.includes(worker)) {
|
|
934
|
+
this.activeTasks.delete(id);
|
|
935
|
+
this.clearTaskTimer(task);
|
|
936
|
+
task.reject(/* @__PURE__ */ new Error(`Worker error: ${error.message}`));
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
processQueue() {
|
|
940
|
+
while (this.taskQueue.length > 0 && this.availableWorkers.length > 0) {
|
|
941
|
+
const task = this.taskQueue.shift();
|
|
942
|
+
const worker = this.availableWorkers.shift();
|
|
943
|
+
this.executeTask(worker, task);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
executeTask(worker, task) {
|
|
947
|
+
this.activeTasks.set(task.id, task);
|
|
948
|
+
task.worker = worker;
|
|
949
|
+
const message = {
|
|
950
|
+
id: task.id,
|
|
951
|
+
type: "task",
|
|
952
|
+
data: task.data
|
|
953
|
+
};
|
|
954
|
+
if (task.transferables && task.transferables.length > 0) worker.postMessage(message, task.transferables);
|
|
955
|
+
else worker.postMessage(message);
|
|
956
|
+
}
|
|
957
|
+
async handleTaskTimeout(task, timeoutMs) {
|
|
958
|
+
const queuedAt = this.taskQueue.indexOf(task);
|
|
959
|
+
if (queuedAt >= 0) {
|
|
960
|
+
this.taskQueue.splice(queuedAt, 1);
|
|
961
|
+
task.reject(/* @__PURE__ */ new Error(`Worker task timed out after ${timeoutMs}ms`));
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
if (!this.activeTasks.has(task.id)) return;
|
|
965
|
+
this.activeTasks.delete(task.id);
|
|
966
|
+
const dead = task.worker;
|
|
967
|
+
if (dead) {
|
|
968
|
+
try {
|
|
969
|
+
dead.terminate();
|
|
970
|
+
} catch {}
|
|
971
|
+
this.workers = this.workers.filter((w) => w !== dead);
|
|
972
|
+
this.availableWorkers = this.availableWorkers.filter((w) => w !== dead);
|
|
973
|
+
try {
|
|
974
|
+
const replacement = await this.createWorker();
|
|
975
|
+
if (this.terminated) {
|
|
976
|
+
replacement.terminate();
|
|
977
|
+
task.reject(/* @__PURE__ */ new Error(`Worker task timed out after ${timeoutMs}ms`));
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
this.workers.push(replacement);
|
|
981
|
+
this.availableWorkers.push(replacement);
|
|
982
|
+
this.processQueue();
|
|
983
|
+
} catch (err) {
|
|
984
|
+
console.error("WorkerPool: failed to spawn replacement worker:", err);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
task.reject(/* @__PURE__ */ new Error(`Worker task timed out after ${timeoutMs}ms`));
|
|
988
|
+
}
|
|
989
|
+
async execute(data, transferables, timeoutMs) {
|
|
990
|
+
return new Promise((resolve, reject) => {
|
|
991
|
+
if (this.terminated) {
|
|
992
|
+
reject(/* @__PURE__ */ new Error("WorkerPool terminated"));
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
const task = {
|
|
996
|
+
id: this.generateTaskId(),
|
|
997
|
+
data,
|
|
998
|
+
resolve: (value) => resolve(value),
|
|
999
|
+
reject,
|
|
1000
|
+
transferables
|
|
1001
|
+
};
|
|
1002
|
+
if (typeof timeoutMs === "number" && timeoutMs > 0) task.timeoutHandle = setTimeout(() => {
|
|
1003
|
+
this.handleTaskTimeout(task, timeoutMs);
|
|
1004
|
+
}, timeoutMs);
|
|
1005
|
+
if (this.availableWorkers.length > 0) {
|
|
1006
|
+
const worker = this.availableWorkers.shift();
|
|
1007
|
+
this.executeTask(worker, task);
|
|
1008
|
+
} else this.taskQueue.push(task);
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
generateTaskId() {
|
|
1012
|
+
return `task_${Date.now()}_${globalThis.crypto.randomUUID()}`;
|
|
1013
|
+
}
|
|
1014
|
+
async terminate() {
|
|
1015
|
+
this.terminated = true;
|
|
1016
|
+
for (const task of this.taskQueue) {
|
|
1017
|
+
this.clearTaskTimer(task);
|
|
1018
|
+
task.reject(/* @__PURE__ */ new Error("WorkerPool terminated"));
|
|
1019
|
+
}
|
|
1020
|
+
this.taskQueue = [];
|
|
1021
|
+
for (const task of this.activeTasks.values()) {
|
|
1022
|
+
this.clearTaskTimer(task);
|
|
1023
|
+
task.reject(/* @__PURE__ */ new Error("WorkerPool terminated"));
|
|
1024
|
+
}
|
|
1025
|
+
this.activeTasks.clear();
|
|
1026
|
+
for (const worker of this.workers) worker.terminate();
|
|
1027
|
+
this.workers = [];
|
|
1028
|
+
this.availableWorkers = [];
|
|
1029
|
+
}
|
|
1030
|
+
clearTaskTimer(task) {
|
|
1031
|
+
if (task.timeoutHandle !== void 0) {
|
|
1032
|
+
clearTimeout(task.timeoutHandle);
|
|
1033
|
+
task.timeoutHandle = void 0;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
get activeTaskCount() {
|
|
1037
|
+
return this.activeTasks.size;
|
|
1038
|
+
}
|
|
1039
|
+
get queuedTaskCount() {
|
|
1040
|
+
return this.taskQueue.length;
|
|
1041
|
+
}
|
|
1042
|
+
get workerCount() {
|
|
1043
|
+
return this.workers.length;
|
|
1044
|
+
}
|
|
1493
1045
|
};
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
// src/operations/matmul.ts
|
|
1046
|
+
//#endregion
|
|
1047
|
+
//#region src/operations/matmul.ts
|
|
1497
1048
|
async function parallelMatmul(a, aRows, aCols, b, bCols, options = {}) {
|
|
1498
|
-
|
|
1499
|
-
return pool.matmul(a, aRows, aCols, b, bCols);
|
|
1049
|
+
return (options.pool ?? computePool).matmul(a, aRows, aCols, b, bCols);
|
|
1500
1050
|
}
|
|
1501
1051
|
async function parallelMatvec(matrix, rows, cols, vector, options = {}) {
|
|
1502
|
-
|
|
1503
|
-
return pool.matvec(matrix, rows, cols, vector);
|
|
1052
|
+
return (options.pool ?? computePool).matvec(matrix, rows, cols, vector);
|
|
1504
1053
|
}
|
|
1505
1054
|
async function parallelTranspose(data, rows, cols, options = {}) {
|
|
1506
|
-
|
|
1507
|
-
return pool.transpose(data, rows, cols);
|
|
1055
|
+
return (options.pool ?? computePool).transpose(data, rows, cols);
|
|
1508
1056
|
}
|
|
1509
1057
|
async function parallelOuter(a, b, options = {}) {
|
|
1510
|
-
|
|
1511
|
-
return pool.outer(a, b);
|
|
1058
|
+
return (options.pool ?? computePool).outer(a, b);
|
|
1512
1059
|
}
|
|
1513
1060
|
async function parallelDot(a, b, options = {}) {
|
|
1514
|
-
|
|
1515
|
-
return pool.dot(a, b);
|
|
1061
|
+
return (options.pool ?? computePool).dot(a, b);
|
|
1516
1062
|
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1063
|
+
//#endregion
|
|
1064
|
+
//#region src/operations/elementwise.ts
|
|
1519
1065
|
async function parallelAdd(a, b, options = {}) {
|
|
1520
|
-
|
|
1521
|
-
return pool.add(a, b);
|
|
1066
|
+
return (options.pool ?? computePool).add(a, b);
|
|
1522
1067
|
}
|
|
1523
1068
|
async function parallelSubtract(a, b, options = {}) {
|
|
1524
|
-
|
|
1525
|
-
return pool.subtract(a, b);
|
|
1069
|
+
return (options.pool ?? computePool).subtract(a, b);
|
|
1526
1070
|
}
|
|
1527
1071
|
async function parallelMultiply(a, b, options = {}) {
|
|
1528
|
-
|
|
1529
|
-
return pool.multiply(a, b);
|
|
1072
|
+
return (options.pool ?? computePool).multiply(a, b);
|
|
1530
1073
|
}
|
|
1531
1074
|
async function parallelDivide(a, b, options = {}) {
|
|
1532
|
-
|
|
1533
|
-
return pool.divide(a, b);
|
|
1075
|
+
return (options.pool ?? computePool).divide(a, b);
|
|
1534
1076
|
}
|
|
1535
1077
|
async function parallelScale(data, scalar, options = {}) {
|
|
1536
|
-
|
|
1537
|
-
return pool.scale(data, scalar);
|
|
1078
|
+
return (options.pool ?? computePool).scale(data, scalar);
|
|
1538
1079
|
}
|
|
1539
1080
|
async function parallelAbs(data, options = {}) {
|
|
1540
|
-
|
|
1541
|
-
return pool.abs(data);
|
|
1081
|
+
return (options.pool ?? computePool).abs(data);
|
|
1542
1082
|
}
|
|
1543
1083
|
async function parallelNegate(data, options = {}) {
|
|
1544
|
-
|
|
1545
|
-
return pool.negate(data);
|
|
1084
|
+
return (options.pool ?? computePool).negate(data);
|
|
1546
1085
|
}
|
|
1547
1086
|
async function parallelSquare(data, options = {}) {
|
|
1548
|
-
|
|
1549
|
-
return pool.square(data);
|
|
1087
|
+
return (options.pool ?? computePool).square(data);
|
|
1550
1088
|
}
|
|
1551
1089
|
async function parallelSqrt(data, options = {}) {
|
|
1552
|
-
|
|
1553
|
-
return pool.sqrt(data);
|
|
1090
|
+
return (options.pool ?? computePool).sqrt(data);
|
|
1554
1091
|
}
|
|
1555
1092
|
async function parallelExp(data, options = {}) {
|
|
1556
|
-
|
|
1557
|
-
return pool.exp(data);
|
|
1093
|
+
return (options.pool ?? computePool).exp(data);
|
|
1558
1094
|
}
|
|
1559
1095
|
async function parallelLog(data, options = {}) {
|
|
1560
|
-
|
|
1561
|
-
return pool.log(data);
|
|
1096
|
+
return (options.pool ?? computePool).log(data);
|
|
1562
1097
|
}
|
|
1563
1098
|
async function parallelSin(data, options = {}) {
|
|
1564
|
-
|
|
1565
|
-
return pool.sin(data);
|
|
1099
|
+
return (options.pool ?? computePool).sin(data);
|
|
1566
1100
|
}
|
|
1567
1101
|
async function parallelCos(data, options = {}) {
|
|
1568
|
-
|
|
1569
|
-
return pool.cos(data);
|
|
1102
|
+
return (options.pool ?? computePool).cos(data);
|
|
1570
1103
|
}
|
|
1571
1104
|
async function parallelTan(data, options = {}) {
|
|
1572
|
-
|
|
1573
|
-
return pool.tan(data);
|
|
1105
|
+
return (options.pool ?? computePool).tan(data);
|
|
1574
1106
|
}
|
|
1575
1107
|
async function parallelElementwise(a, b, op, options = {}) {
|
|
1576
|
-
|
|
1577
|
-
return pool.elementwise(a, b, op);
|
|
1108
|
+
return (options.pool ?? computePool).elementwise(a, b, op);
|
|
1578
1109
|
}
|
|
1579
1110
|
async function parallelUnary(data, fn, options = {}) {
|
|
1580
|
-
|
|
1581
|
-
return pool.unary(data, fn);
|
|
1111
|
+
return (options.pool ?? computePool).unary(data, fn);
|
|
1582
1112
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1113
|
+
//#endregion
|
|
1114
|
+
//#region src/operations/reduce.ts
|
|
1585
1115
|
async function parallelSum(data, options = {}) {
|
|
1586
|
-
|
|
1587
|
-
return pool.sum(data);
|
|
1116
|
+
return (options.pool ?? computePool).sum(data);
|
|
1588
1117
|
}
|
|
1589
1118
|
async function parallelMean(data, options = {}) {
|
|
1590
|
-
|
|
1591
|
-
return pool.mean(data);
|
|
1119
|
+
return (options.pool ?? computePool).mean(data);
|
|
1592
1120
|
}
|
|
1593
1121
|
async function parallelMin(data, options = {}) {
|
|
1594
|
-
|
|
1595
|
-
return pool.min(data);
|
|
1122
|
+
return (options.pool ?? computePool).min(data);
|
|
1596
1123
|
}
|
|
1597
1124
|
async function parallelMax(data, options = {}) {
|
|
1598
|
-
|
|
1599
|
-
return pool.max(data);
|
|
1125
|
+
return (options.pool ?? computePool).max(data);
|
|
1600
1126
|
}
|
|
1601
1127
|
async function parallelMinMax(data, options = {}) {
|
|
1602
|
-
|
|
1603
|
-
return pool.minMax(data);
|
|
1128
|
+
return (options.pool ?? computePool).minMax(data);
|
|
1604
1129
|
}
|
|
1605
1130
|
async function parallelVariance(data, options = {}) {
|
|
1606
|
-
|
|
1607
|
-
return pool.variance(data);
|
|
1131
|
+
return (options.pool ?? computePool).variance(data);
|
|
1608
1132
|
}
|
|
1609
1133
|
async function parallelStd(data, options = {}) {
|
|
1610
|
-
|
|
1611
|
-
return pool.std(data);
|
|
1134
|
+
return (options.pool ?? computePool).std(data);
|
|
1612
1135
|
}
|
|
1613
1136
|
async function parallelNorm(data, options = {}) {
|
|
1614
|
-
|
|
1615
|
-
return pool.norm(data);
|
|
1137
|
+
return (options.pool ?? computePool).norm(data);
|
|
1616
1138
|
}
|
|
1617
1139
|
async function parallelDistance(a, b, options = {}) {
|
|
1618
|
-
|
|
1619
|
-
return pool.distance(a, b);
|
|
1140
|
+
return (options.pool ?? computePool).distance(a, b);
|
|
1620
1141
|
}
|
|
1621
1142
|
async function parallelHistogram(data, bins, min, max, options = {}) {
|
|
1622
|
-
|
|
1623
|
-
return pool.histogram(data, bins, min, max);
|
|
1143
|
+
return (options.pool ?? computePool).histogram(data, bins, min, max);
|
|
1624
1144
|
}
|
|
1625
1145
|
async function parallelReduce(data, fn, initial, options = {}) {
|
|
1626
|
-
|
|
1627
|
-
return pool.reduce(data, fn, initial);
|
|
1146
|
+
return (options.pool ?? computePool).reduce(data, fn, initial);
|
|
1628
1147
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1148
|
+
//#endregion
|
|
1149
|
+
//#region src/operations/map.ts
|
|
1631
1150
|
async function parallelMap(data, fn, options = {}) {
|
|
1632
|
-
|
|
1633
|
-
return pool.map(data, fn);
|
|
1151
|
+
return (options.pool ?? computePool).map(data, fn);
|
|
1634
1152
|
}
|
|
1635
1153
|
async function parallelFilter(data, predicate, options = {}) {
|
|
1636
|
-
|
|
1637
|
-
return pool.filter(data, predicate);
|
|
1154
|
+
return (options.pool ?? computePool).filter(data, predicate);
|
|
1638
1155
|
}
|
|
1639
1156
|
async function parallelFind(data, predicate, options = {}) {
|
|
1640
|
-
|
|
1641
|
-
return pool.find(data, predicate);
|
|
1157
|
+
return (options.pool ?? computePool).find(data, predicate);
|
|
1642
1158
|
}
|
|
1643
1159
|
async function parallelSort(data, compare, options = {}) {
|
|
1644
|
-
|
|
1645
|
-
return pool.sort(data, compare);
|
|
1160
|
+
return (options.pool ?? computePool).sort(data, compare);
|
|
1646
1161
|
}
|
|
1647
1162
|
async function parallelForEach(data, fn, options = {}) {
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
result: result.result
|
|
1656
|
-
};
|
|
1163
|
+
const result = await (options.pool ?? computePool).map(data, (item) => {
|
|
1164
|
+
fn(item);
|
|
1165
|
+
});
|
|
1166
|
+
return {
|
|
1167
|
+
...result,
|
|
1168
|
+
result: result.result
|
|
1169
|
+
};
|
|
1657
1170
|
}
|
|
1658
1171
|
async function parallelSome(data, predicate, options = {}) {
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
};
|
|
1172
|
+
const findResult = await (options.pool ?? computePool).find(data, predicate);
|
|
1173
|
+
return {
|
|
1174
|
+
...findResult,
|
|
1175
|
+
result: findResult.result.found
|
|
1176
|
+
};
|
|
1665
1177
|
}
|
|
1666
1178
|
async function parallelEvery(data, predicate, options = {}) {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
};
|
|
1179
|
+
const findResult = await (options.pool ?? computePool).find(data, (item) => !predicate(item));
|
|
1180
|
+
return {
|
|
1181
|
+
...findResult,
|
|
1182
|
+
result: !findResult.result.found
|
|
1183
|
+
};
|
|
1673
1184
|
}
|
|
1674
1185
|
async function parallelCount(data, predicate, options = {}) {
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
// src/strategies/threshold.ts
|
|
1186
|
+
const pool = options.pool ?? computePool;
|
|
1187
|
+
if (!predicate) return {
|
|
1188
|
+
result: data.length,
|
|
1189
|
+
duration: 0,
|
|
1190
|
+
chunks: 1,
|
|
1191
|
+
parallelized: false
|
|
1192
|
+
};
|
|
1193
|
+
const filterResult = await pool.filter(data, predicate);
|
|
1194
|
+
return {
|
|
1195
|
+
...filterResult,
|
|
1196
|
+
result: filterResult.result.length
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
//#endregion
|
|
1200
|
+
//#region src/strategies/threshold.ts
|
|
1692
1201
|
function opThreshold(op, fallback) {
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
// ~10K elements
|
|
1705
|
-
sort: 5e3,
|
|
1706
|
-
// ~5K elements
|
|
1707
|
-
decomposition: 2500,
|
|
1708
|
-
// ~50x50 matrix (no single canonical op; heuristic)
|
|
1709
|
-
general: 5e4
|
|
1710
|
-
// Default ~50K
|
|
1202
|
+
const raw = DEFAULT_THRESHOLD_BY_OP[op];
|
|
1203
|
+
return typeof raw === "number" ? raw : fallback;
|
|
1204
|
+
}
|
|
1205
|
+
const DEFAULT_THRESHOLDS = {
|
|
1206
|
+
matmul: opThreshold("matmul", 4096),
|
|
1207
|
+
elementwise: 5e4,
|
|
1208
|
+
reduce: 1e5,
|
|
1209
|
+
map: 1e4,
|
|
1210
|
+
sort: 5e3,
|
|
1211
|
+
decomposition: 2500,
|
|
1212
|
+
general: 5e4
|
|
1711
1213
|
};
|
|
1712
1214
|
var ThresholdDispatcher = class {
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
shouldParallelize(elementCount, category = "general") {
|
|
1779
|
-
return this.dispatch(elementCount, category).mode === "parallel";
|
|
1780
|
-
}
|
|
1781
|
-
/**
|
|
1782
|
-
* Calculate optimal chunk count based on operation and data size
|
|
1783
|
-
*/
|
|
1784
|
-
calculateChunks(elementCount, category = "general") {
|
|
1785
|
-
const dispatch2 = this.dispatch(elementCount, category);
|
|
1786
|
-
if (dispatch2.mode === "sequential") {
|
|
1787
|
-
return 1;
|
|
1788
|
-
}
|
|
1789
|
-
const stats = this.pool.stats();
|
|
1790
|
-
const workerCount = Math.max(1, stats.totalWorkers);
|
|
1791
|
-
switch (category) {
|
|
1792
|
-
case "matmul":
|
|
1793
|
-
case "decomposition":
|
|
1794
|
-
return workerCount;
|
|
1795
|
-
case "elementwise":
|
|
1796
|
-
case "map":
|
|
1797
|
-
return Math.min(workerCount * 2, Math.ceil(elementCount / 1e4));
|
|
1798
|
-
case "reduce":
|
|
1799
|
-
return Math.min(workerCount, Math.ceil(elementCount / 5e4));
|
|
1800
|
-
case "sort":
|
|
1801
|
-
return Math.min(workerCount, 8);
|
|
1802
|
-
default:
|
|
1803
|
-
return workerCount;
|
|
1804
|
-
}
|
|
1805
|
-
}
|
|
1215
|
+
thresholds;
|
|
1216
|
+
pool;
|
|
1217
|
+
constructor(thresholds = {}, pool) {
|
|
1218
|
+
this.thresholds = {
|
|
1219
|
+
...DEFAULT_THRESHOLDS,
|
|
1220
|
+
...thresholds
|
|
1221
|
+
};
|
|
1222
|
+
this.pool = pool ?? computePool;
|
|
1223
|
+
}
|
|
1224
|
+
getThreshold(category) {
|
|
1225
|
+
return this.thresholds[category];
|
|
1226
|
+
}
|
|
1227
|
+
setThresholds(thresholds) {
|
|
1228
|
+
this.thresholds = {
|
|
1229
|
+
...this.thresholds,
|
|
1230
|
+
...thresholds
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
getThresholds() {
|
|
1234
|
+
return { ...this.thresholds };
|
|
1235
|
+
}
|
|
1236
|
+
dispatch(elementCount, category = "general") {
|
|
1237
|
+
const threshold = this.thresholds[category];
|
|
1238
|
+
if (!this.pool.isReady()) return {
|
|
1239
|
+
mode: "sequential",
|
|
1240
|
+
reason: "Worker pool not initialized",
|
|
1241
|
+
threshold,
|
|
1242
|
+
elementCount
|
|
1243
|
+
};
|
|
1244
|
+
if (!this.pool.getConfig().enabled) return {
|
|
1245
|
+
mode: "sequential",
|
|
1246
|
+
reason: "Parallel processing disabled",
|
|
1247
|
+
threshold,
|
|
1248
|
+
elementCount
|
|
1249
|
+
};
|
|
1250
|
+
if (elementCount < threshold) return {
|
|
1251
|
+
mode: "sequential",
|
|
1252
|
+
reason: `Element count (${elementCount}) below threshold (${threshold})`,
|
|
1253
|
+
threshold,
|
|
1254
|
+
elementCount
|
|
1255
|
+
};
|
|
1256
|
+
return {
|
|
1257
|
+
mode: "parallel",
|
|
1258
|
+
reason: `Element count (${elementCount}) exceeds threshold (${threshold})`,
|
|
1259
|
+
threshold,
|
|
1260
|
+
elementCount
|
|
1261
|
+
};
|
|
1262
|
+
}
|
|
1263
|
+
shouldParallelize(elementCount, category = "general") {
|
|
1264
|
+
return this.dispatch(elementCount, category).mode === "parallel";
|
|
1265
|
+
}
|
|
1266
|
+
calculateChunks(elementCount, category = "general") {
|
|
1267
|
+
if (this.dispatch(elementCount, category).mode === "sequential") return 1;
|
|
1268
|
+
const stats = this.pool.stats();
|
|
1269
|
+
const workerCount = Math.max(1, stats.totalWorkers);
|
|
1270
|
+
switch (category) {
|
|
1271
|
+
case "matmul":
|
|
1272
|
+
case "decomposition": return workerCount;
|
|
1273
|
+
case "elementwise":
|
|
1274
|
+
case "map": return Math.min(workerCount * 2, Math.ceil(elementCount / 1e4));
|
|
1275
|
+
case "reduce": return Math.min(workerCount, Math.ceil(elementCount / 5e4));
|
|
1276
|
+
case "sort": return Math.min(workerCount, 8);
|
|
1277
|
+
default: return workerCount;
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1806
1280
|
};
|
|
1807
|
-
|
|
1808
|
-
function
|
|
1809
|
-
|
|
1281
|
+
const thresholdDispatcher = new ThresholdDispatcher();
|
|
1282
|
+
function shouldParallelize$1(elementCount, category = "general") {
|
|
1283
|
+
return thresholdDispatcher.shouldParallelize(elementCount, category);
|
|
1810
1284
|
}
|
|
1811
1285
|
function dispatch(elementCount, category = "general") {
|
|
1812
|
-
|
|
1286
|
+
return thresholdDispatcher.dispatch(elementCount, category);
|
|
1813
1287
|
}
|
|
1814
1288
|
function calculateChunks(elementCount, category = "general") {
|
|
1815
|
-
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
DEFAULT_POOL_CONFIG,
|
|
1820
|
-
DEFAULT_THRESHOLDS,
|
|
1821
|
-
DEFAULT_THRESHOLD_BY_OP,
|
|
1822
|
-
ThresholdDispatcher,
|
|
1823
|
-
Transfer,
|
|
1824
|
-
bitAnd,
|
|
1825
|
-
bitNot,
|
|
1826
|
-
bitOr,
|
|
1827
|
-
bitXor,
|
|
1828
|
-
calculateChunks,
|
|
1829
|
-
calculateOptimalChunks,
|
|
1830
|
-
chunkArray,
|
|
1831
|
-
chunkFloat64Array,
|
|
1832
|
-
computePool,
|
|
1833
|
-
dispatch,
|
|
1834
|
-
leftShift,
|
|
1835
|
-
mergeArrayChunks,
|
|
1836
|
-
mergeFloat64Chunks,
|
|
1837
|
-
parallelAbs,
|
|
1838
|
-
parallelAdd,
|
|
1839
|
-
parallelCos,
|
|
1840
|
-
parallelCount,
|
|
1841
|
-
parallelDistance,
|
|
1842
|
-
parallelDivide,
|
|
1843
|
-
parallelDot,
|
|
1844
|
-
parallelElementwise,
|
|
1845
|
-
parallelEvery,
|
|
1846
|
-
parallelExp,
|
|
1847
|
-
parallelFilter,
|
|
1848
|
-
parallelFind,
|
|
1849
|
-
parallelForEach,
|
|
1850
|
-
parallelHistogram,
|
|
1851
|
-
parallelLog,
|
|
1852
|
-
parallelMap,
|
|
1853
|
-
parallelMatmul,
|
|
1854
|
-
parallelMatvec,
|
|
1855
|
-
parallelMax,
|
|
1856
|
-
parallelMean,
|
|
1857
|
-
parallelMin,
|
|
1858
|
-
parallelMinMax,
|
|
1859
|
-
parallelMultiply,
|
|
1860
|
-
parallelNegate,
|
|
1861
|
-
parallelNorm,
|
|
1862
|
-
parallelOuter,
|
|
1863
|
-
parallelReduce,
|
|
1864
|
-
parallelScale,
|
|
1865
|
-
parallelSin,
|
|
1866
|
-
parallelSome,
|
|
1867
|
-
parallelSort,
|
|
1868
|
-
parallelSqrt,
|
|
1869
|
-
parallelSquare,
|
|
1870
|
-
parallelStd,
|
|
1871
|
-
parallelSubtract,
|
|
1872
|
-
parallelSum,
|
|
1873
|
-
parallelTan,
|
|
1874
|
-
parallelTranspose,
|
|
1875
|
-
parallelUnary,
|
|
1876
|
-
parallelVariance,
|
|
1877
|
-
partition2D,
|
|
1878
|
-
partitionRange,
|
|
1879
|
-
resolveOpThreshold,
|
|
1880
|
-
rightArithShift,
|
|
1881
|
-
rightLogShift,
|
|
1882
|
-
shouldParallelize as shouldChunkParallelize,
|
|
1883
|
-
shouldParallelize2 as shouldParallelize,
|
|
1884
|
-
thresholdDispatcher
|
|
1885
|
-
};
|
|
1289
|
+
return thresholdDispatcher.calculateChunks(elementCount, category);
|
|
1290
|
+
}
|
|
1291
|
+
//#endregion
|
|
1292
|
+
export { ComputePool, DEFAULT_POOL_CONFIG, DEFAULT_THRESHOLDS, DEFAULT_THRESHOLD_BY_OP, ThresholdDispatcher, Transfer, WorkerPool, bitAnd, bitNot, bitOr, bitXor, calculateChunks, calculateOptimalChunks, chunkArray, chunkFloat64Array, computePool, dispatch, leftShift, mergeArrayChunks, mergeFloat64Chunks, parallelAbs, parallelAdd, parallelCos, parallelCount, parallelDistance, parallelDivide, parallelDot, parallelElementwise, parallelEvery, parallelExp, parallelFilter, parallelFind, parallelForEach, parallelHistogram, parallelLog, parallelMap, parallelMatmul, parallelMatvec, parallelMax, parallelMean, parallelMin, parallelMinMax, parallelMultiply, parallelNegate, parallelNorm, parallelOuter, parallelReduce, parallelScale, parallelSin, parallelSome, parallelSort, parallelSqrt, parallelSquare, parallelStd, parallelSubtract, parallelSum, parallelTan, parallelTranspose, parallelUnary, parallelVariance, partition2D, partitionRange, resolveOpThreshold, rightArithShift, rightLogShift, shouldParallelize as shouldChunkParallelize, shouldParallelize$1 as shouldParallelize, thresholdDispatcher };
|