@sdeverywhere/runtime-async 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/index.cjs +138 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +111 -0
- package/dist/index.js.map +1 -0
- package/dist/runner.cjs +83 -0
- package/dist/runner.cjs.map +1 -0
- package/dist/runner.d.ts +44 -0
- package/dist/runner.js +59 -0
- package/dist/runner.js.map +1 -0
- package/dist/worker.cjs +78 -0
- package/dist/worker.cjs.map +1 -0
- package/dist/worker.d.ts +14 -0
- package/dist/worker.js +54 -0
- package/dist/worker.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-2022 Climate Interactive / New Venture Fund
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @sdeverywhere/runtime-async
|
|
2
|
+
|
|
3
|
+
This package provides an implementation of the `ModelRunner` interface from
|
|
4
|
+
the `@sdeverywhere/runtime` package that uses a Web Worker (in the browser)
|
|
5
|
+
or a worker thread (in a Node.js app) to run the model asynchronously off
|
|
6
|
+
the main JavaScript thread.
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
### 1. Initialize the `WasmModel` in a Web Worker
|
|
11
|
+
|
|
12
|
+
In your app project, define a separate JavaScript file, called
|
|
13
|
+
`worker.js` for example, that initializes the model worker in the
|
|
14
|
+
context of the Web Worker:
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import { initWasmModelAndBuffers } from '@sdeverywhere/runtime'
|
|
18
|
+
import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'
|
|
19
|
+
|
|
20
|
+
async function initWasmModel() {
|
|
21
|
+
const wasmModules = loadWasm()
|
|
22
|
+
return initWasmModelAndBuffers(...)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
exposeModelWorker(initWasmModel)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 2. Spawn the worker
|
|
29
|
+
|
|
30
|
+
In your web app, call the `spawnAsyncModelRunner` function, which will
|
|
31
|
+
spawn the Web Worker and initialize the `ModelRunner` that communicates
|
|
32
|
+
with the worker:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'
|
|
36
|
+
|
|
37
|
+
async function initApp() {
|
|
38
|
+
// ...
|
|
39
|
+
const runner = await spawnAsyncModelRunner({ path: './worker.js' })
|
|
40
|
+
// ...
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Documentation
|
|
45
|
+
|
|
46
|
+
API documentation is available in the [`docs`](./docs/index.md) directory.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
SDEverywhere is distributed under the MIT license. See `LICENSE` for more details.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/index.ts
|
|
20
|
+
var src_exports = {};
|
|
21
|
+
__export(src_exports, {
|
|
22
|
+
exposeModelWorker: () => exposeModelWorker,
|
|
23
|
+
spawnAsyncModelRunner: () => spawnAsyncModelRunner
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(src_exports);
|
|
26
|
+
|
|
27
|
+
// src/runner.ts
|
|
28
|
+
var import_threads = require("threads");
|
|
29
|
+
async function spawnAsyncModelRunner(workerSpec) {
|
|
30
|
+
if (workerSpec["path"]) {
|
|
31
|
+
return spawnAsyncModelRunnerWithWorker(new import_threads.Worker(workerSpec["path"]));
|
|
32
|
+
} else {
|
|
33
|
+
return spawnAsyncModelRunnerWithWorker(import_threads.BlobWorker.fromText(workerSpec["source"]));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function spawnAsyncModelRunnerWithWorker(worker) {
|
|
37
|
+
const modelWorker2 = await (0, import_threads.spawn)(worker);
|
|
38
|
+
const initResult = await modelWorker2.initModel();
|
|
39
|
+
const rowLength = initResult.rowLength;
|
|
40
|
+
let ioBuffer = initResult.ioBuffer;
|
|
41
|
+
let running = false;
|
|
42
|
+
let terminated = false;
|
|
43
|
+
return {
|
|
44
|
+
runModel: async (inputs, outputs) => {
|
|
45
|
+
if (terminated) {
|
|
46
|
+
throw new Error("Async model runner has already been terminated");
|
|
47
|
+
} else if (running) {
|
|
48
|
+
throw new Error("Async model runner only supports one `runModel` call at a time");
|
|
49
|
+
} else {
|
|
50
|
+
running = true;
|
|
51
|
+
}
|
|
52
|
+
const runTimeLengthInElements = 1;
|
|
53
|
+
const runTimeLengthInBytes = runTimeLengthInElements * 8;
|
|
54
|
+
const inputsLengthInElements = inputs.length;
|
|
55
|
+
const inputsLengthInBytes = inputsLengthInElements * 8;
|
|
56
|
+
const inputsArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements);
|
|
57
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
58
|
+
inputsArray[i] = inputs[i].get();
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
ioBuffer = await modelWorker2.runModel((0, import_threads.Transfer)(ioBuffer));
|
|
62
|
+
} finally {
|
|
63
|
+
running = false;
|
|
64
|
+
}
|
|
65
|
+
const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements);
|
|
66
|
+
outputs.runTimeInMillis = runTimeBufferArray[0];
|
|
67
|
+
const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes;
|
|
68
|
+
const outputsArray = new Float64Array(ioBuffer, outputsOffsetInBytes);
|
|
69
|
+
outputs.updateFromBuffer(outputsArray, rowLength);
|
|
70
|
+
return outputs;
|
|
71
|
+
},
|
|
72
|
+
terminate: () => {
|
|
73
|
+
if (terminated) {
|
|
74
|
+
return Promise.resolve();
|
|
75
|
+
} else {
|
|
76
|
+
terminated = true;
|
|
77
|
+
return import_threads.Thread.terminate(modelWorker2);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/worker.ts
|
|
84
|
+
var import_worker = require("threads/worker");
|
|
85
|
+
var import_runtime = require("@sdeverywhere/runtime");
|
|
86
|
+
var initWasmModel;
|
|
87
|
+
var wasmModel;
|
|
88
|
+
var inputsWasmBuffer;
|
|
89
|
+
var outputsWasmBuffer;
|
|
90
|
+
var modelWorker = {
|
|
91
|
+
async initModel() {
|
|
92
|
+
if (wasmModel) {
|
|
93
|
+
throw new Error("WasmModel was already initialized");
|
|
94
|
+
}
|
|
95
|
+
const result = await initWasmModel();
|
|
96
|
+
wasmModel = result.model;
|
|
97
|
+
inputsWasmBuffer = result.inputsBuffer;
|
|
98
|
+
outputsWasmBuffer = result.outputsBuffer;
|
|
99
|
+
const runTimeLength = 8;
|
|
100
|
+
const inputsLength = inputsWasmBuffer.getArrayView().length;
|
|
101
|
+
const outputsLength = outputsWasmBuffer.getArrayView().length;
|
|
102
|
+
const ioArray = new Float64Array(runTimeLength + inputsLength + outputsLength);
|
|
103
|
+
const rowLength = result.endTime - result.startTime + 1;
|
|
104
|
+
const ioBuffer = ioArray.buffer;
|
|
105
|
+
return (0, import_worker.Transfer)({ rowLength, ioBuffer }, [ioBuffer]);
|
|
106
|
+
},
|
|
107
|
+
runModel(ioBuffer) {
|
|
108
|
+
if (!wasmModel) {
|
|
109
|
+
throw new Error("WasmModel must be initialized before running the model in worker");
|
|
110
|
+
}
|
|
111
|
+
const runTimeLengthInElements = 1;
|
|
112
|
+
const runTimeLengthInBytes = runTimeLengthInElements * 8;
|
|
113
|
+
const inputsWasmArray = inputsWasmBuffer.getArrayView();
|
|
114
|
+
const inputsLengthInElements = inputsWasmArray.length;
|
|
115
|
+
const inputsLengthInBytes = inputsLengthInElements * 8;
|
|
116
|
+
const inputsBufferArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements);
|
|
117
|
+
inputsWasmArray.set(inputsBufferArray);
|
|
118
|
+
const t0 = (0, import_runtime.perfNow)();
|
|
119
|
+
wasmModel.runModel(inputsWasmBuffer, outputsWasmBuffer);
|
|
120
|
+
const elapsed = (0, import_runtime.perfElapsed)(t0);
|
|
121
|
+
const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements);
|
|
122
|
+
runTimeBufferArray[0] = elapsed;
|
|
123
|
+
const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes;
|
|
124
|
+
const outputsBufferArray = new Float64Array(ioBuffer, outputsOffsetInBytes);
|
|
125
|
+
outputsBufferArray.set(outputsWasmBuffer.getArrayView());
|
|
126
|
+
return (0, import_worker.Transfer)(ioBuffer);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
function exposeModelWorker(init) {
|
|
130
|
+
initWasmModel = init;
|
|
131
|
+
(0, import_worker.expose)(modelWorker);
|
|
132
|
+
}
|
|
133
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
134
|
+
0 && (module.exports = {
|
|
135
|
+
exposeModelWorker,
|
|
136
|
+
spawnAsyncModelRunner
|
|
137
|
+
});
|
|
138
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/runner.ts","../src/worker.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nexport { spawnAsyncModelRunner } from './runner'\n\nexport { exposeModelWorker } from './worker'\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport { BlobWorker, spawn, Thread, Transfer, Worker } from 'threads'\n\nimport type { ModelRunner } from '@sdeverywhere/runtime'\n\n/**\n * Initialize a `ModelRunner` that runs the model asynchronously in a worker thread.\n *\n * In your app project, define a JavaScript file, called `worker.js` for example, that\n * initializes the model worker in the context of the Web Worker:\n *\n * ```js\n * import { initWasmModelAndBuffers } from '@sdeverywhere/runtime'\n * import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'\n *\n * async function initWasmModel() {\n * const wasmModules = loadWasm()\n * return initWasmModelAndBuffers(...)\n * }\n *\n * exposeModelWorker(initWasmModel)\n * ```\n *\n * Then, in your web app, call the `spawnAsyncModelRunner` function, which\n * will spawn the Web Worker and initialize the `ModelRunner` that communicates\n * with the worker:\n *\n * ```js\n * import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'\n *\n * async function initApp() {\n * // ...\n * const runner = await spawnAsyncModelRunner({ path: './worker.js' })\n * // ...\n * }\n * ```\n *\n * @param workerSpec Either a `path` to the worker JavaScript file, or the `source`\n * containing the full JavaScript source of the worker.\n */\nexport async function spawnAsyncModelRunner(workerSpec: { path: string } | { source: string }): Promise<ModelRunner> {\n if (workerSpec['path']) {\n return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec['path']))\n } else {\n return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec['source']))\n }\n}\n\n/**\n * @hidden For internal use only\n */\nasync function spawnAsyncModelRunnerWithWorker(worker: Worker): Promise<ModelRunner> {\n // Spawn the given Worker that contains the `ModelWorker`\n const modelWorker = await spawn(worker)\n\n // Wait for the worker to initialize the wasm model (in the worker thread)\n const initResult = await modelWorker.initModel()\n const rowLength: number = initResult.rowLength\n let ioBuffer: ArrayBuffer = initResult.ioBuffer\n\n // Use a flag to ensure that only one request is made at a time\n let running = false\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n return {\n runModel: async (inputs, outputs) => {\n if (terminated) {\n throw new Error('Async model runner has already been terminated')\n } else if (running) {\n throw new Error('Async model runner only supports one `runModel` call at a time')\n } else {\n running = true\n }\n\n // The run time is stored in the first 8 bytes\n const runTimeLengthInElements = 1\n const runTimeLengthInBytes = runTimeLengthInElements * 8\n\n // Capture the current set of input values into the reusable buffer\n const inputsLengthInElements = inputs.length\n const inputsLengthInBytes = inputsLengthInElements * 8\n const inputsArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements)\n for (let i = 0; i < inputs.length; i++) {\n inputsArray[i] = inputs[i].get()\n }\n\n // Run the model in the worker. We pass the underlying `ArrayBuffer`\n // instance back to the worker wrapped in a `Transfer` to make it\n // no-copy transferable, and then the worker will return it back\n // to us.\n try {\n ioBuffer = await modelWorker.runModel(Transfer(ioBuffer))\n } finally {\n running = false\n }\n\n // Save the model run time\n const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements)\n outputs.runTimeInMillis = runTimeBufferArray[0]\n\n // Capture the outputs array by copying the data into the given `Outputs`\n // data structure\n const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes\n const outputsArray = new Float64Array(ioBuffer, outputsOffsetInBytes)\n outputs.updateFromBuffer(outputsArray, rowLength)\n\n return outputs\n },\n\n terminate: () => {\n if (terminated) {\n return Promise.resolve()\n } else {\n terminated = true\n return Thread.terminate(modelWorker)\n }\n }\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { TransferDescriptor } from 'threads'\nimport { expose, Transfer } from 'threads/worker'\n\nimport type { WasmBuffer, WasmModel, WasmModelInitResult } from '@sdeverywhere/runtime'\nimport { perfElapsed, perfNow } from '@sdeverywhere/runtime'\n\n/** @hidden */\nlet initWasmModel: () => Promise<WasmModelInitResult>\n/** @hidden */\nlet wasmModel: WasmModel\n/** @hidden */\nlet inputsWasmBuffer: WasmBuffer\n/** @hidden */\nlet outputsWasmBuffer: WasmBuffer\n\ninterface InitResult {\n rowLength: number\n ioBuffer: ArrayBuffer\n}\n\n/** @hidden */\nconst modelWorker = {\n async initModel(): Promise<TransferDescriptor<InitResult>> {\n if (wasmModel) {\n throw new Error('WasmModel was already initialized')\n }\n\n // Initialize the wasm model and associated buffers\n const result = await initWasmModel()\n\n // Capture the `WasmModel` instance and `WasmBuffer` instances\n wasmModel = result.model\n inputsWasmBuffer = result.inputsBuffer\n outputsWasmBuffer = result.outputsBuffer\n\n // Create a combined array that will hold a copy of the inputs and outputs\n // wasm buffers; this buffer is no-copy transferable, whereas the wasm ones\n // are not allowed to be transferred\n const runTimeLength = 8\n const inputsLength = inputsWasmBuffer.getArrayView().length\n const outputsLength = outputsWasmBuffer.getArrayView().length\n const ioArray = new Float64Array(runTimeLength + inputsLength + outputsLength)\n\n // The row length is the number of elements in each row of the outputs buffer\n const rowLength = result.endTime - result.startTime + 1\n\n // Transfer the underlying buffer to the runner\n const ioBuffer = ioArray.buffer\n return Transfer({ rowLength, ioBuffer }, [ioBuffer])\n },\n\n runModel(ioBuffer: ArrayBuffer): TransferDescriptor<ArrayBuffer> {\n if (!wasmModel) {\n throw new Error('WasmModel must be initialized before running the model in worker')\n }\n\n // The run time is stored in the first 8 bytes\n const runTimeLengthInElements = 1\n const runTimeLengthInBytes = runTimeLengthInElements * 8\n\n // Copy the inputs into the wasm inputs buffer\n const inputsWasmArray = inputsWasmBuffer.getArrayView()\n const inputsLengthInElements = inputsWasmArray.length\n const inputsLengthInBytes = inputsLengthInElements * 8\n const inputsBufferArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements)\n inputsWasmArray.set(inputsBufferArray)\n\n // Run the model using the wasm buffers\n const t0 = perfNow()\n wasmModel.runModel(inputsWasmBuffer, outputsWasmBuffer)\n const elapsed = perfElapsed(t0)\n\n // Write the model run time to the buffer\n const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements)\n runTimeBufferArray[0] = elapsed\n\n // Copy the outputs from the wasm outputs buffer\n const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes\n const outputsBufferArray = new Float64Array(ioBuffer, outputsOffsetInBytes)\n outputsBufferArray.set(outputsWasmBuffer.getArrayView())\n\n // Transfer the buffer back to the runner\n return Transfer(ioBuffer)\n }\n}\n\n/**\n * Expose an object in the current worker thread that communicates with the\n * `ModelRunner` instance running in the main thread. The exposed worker\n * object will take care of running the `WasmModel` on the worker thread\n * and sending the outputs back to the main process.\n *\n * @param init The function that initializes the `WasmModel` instance that\n * is used in the worker thread.\n */\nexport function exposeModelWorker(init: () => Promise<WasmModelInitResult>): void {\n // Save the initializer, which will be used when the runner calls `initModel`\n // on the worker\n initWasmModel = init\n\n // Expose the worker implementation to `threads.js`\n expose(modelWorker)\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// src/runner.ts
|
|
2
|
+
import { BlobWorker, spawn, Thread, Transfer, Worker } from "threads";
|
|
3
|
+
async function spawnAsyncModelRunner(workerSpec) {
|
|
4
|
+
if (workerSpec["path"]) {
|
|
5
|
+
return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec["path"]));
|
|
6
|
+
} else {
|
|
7
|
+
return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec["source"]));
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
async function spawnAsyncModelRunnerWithWorker(worker) {
|
|
11
|
+
const modelWorker2 = await spawn(worker);
|
|
12
|
+
const initResult = await modelWorker2.initModel();
|
|
13
|
+
const rowLength = initResult.rowLength;
|
|
14
|
+
let ioBuffer = initResult.ioBuffer;
|
|
15
|
+
let running = false;
|
|
16
|
+
let terminated = false;
|
|
17
|
+
return {
|
|
18
|
+
runModel: async (inputs, outputs) => {
|
|
19
|
+
if (terminated) {
|
|
20
|
+
throw new Error("Async model runner has already been terminated");
|
|
21
|
+
} else if (running) {
|
|
22
|
+
throw new Error("Async model runner only supports one `runModel` call at a time");
|
|
23
|
+
} else {
|
|
24
|
+
running = true;
|
|
25
|
+
}
|
|
26
|
+
const runTimeLengthInElements = 1;
|
|
27
|
+
const runTimeLengthInBytes = runTimeLengthInElements * 8;
|
|
28
|
+
const inputsLengthInElements = inputs.length;
|
|
29
|
+
const inputsLengthInBytes = inputsLengthInElements * 8;
|
|
30
|
+
const inputsArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements);
|
|
31
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
32
|
+
inputsArray[i] = inputs[i].get();
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
ioBuffer = await modelWorker2.runModel(Transfer(ioBuffer));
|
|
36
|
+
} finally {
|
|
37
|
+
running = false;
|
|
38
|
+
}
|
|
39
|
+
const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements);
|
|
40
|
+
outputs.runTimeInMillis = runTimeBufferArray[0];
|
|
41
|
+
const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes;
|
|
42
|
+
const outputsArray = new Float64Array(ioBuffer, outputsOffsetInBytes);
|
|
43
|
+
outputs.updateFromBuffer(outputsArray, rowLength);
|
|
44
|
+
return outputs;
|
|
45
|
+
},
|
|
46
|
+
terminate: () => {
|
|
47
|
+
if (terminated) {
|
|
48
|
+
return Promise.resolve();
|
|
49
|
+
} else {
|
|
50
|
+
terminated = true;
|
|
51
|
+
return Thread.terminate(modelWorker2);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/worker.ts
|
|
58
|
+
import { expose, Transfer as Transfer2 } from "threads/worker";
|
|
59
|
+
import { perfElapsed, perfNow } from "@sdeverywhere/runtime";
|
|
60
|
+
var initWasmModel;
|
|
61
|
+
var wasmModel;
|
|
62
|
+
var inputsWasmBuffer;
|
|
63
|
+
var outputsWasmBuffer;
|
|
64
|
+
var modelWorker = {
|
|
65
|
+
async initModel() {
|
|
66
|
+
if (wasmModel) {
|
|
67
|
+
throw new Error("WasmModel was already initialized");
|
|
68
|
+
}
|
|
69
|
+
const result = await initWasmModel();
|
|
70
|
+
wasmModel = result.model;
|
|
71
|
+
inputsWasmBuffer = result.inputsBuffer;
|
|
72
|
+
outputsWasmBuffer = result.outputsBuffer;
|
|
73
|
+
const runTimeLength = 8;
|
|
74
|
+
const inputsLength = inputsWasmBuffer.getArrayView().length;
|
|
75
|
+
const outputsLength = outputsWasmBuffer.getArrayView().length;
|
|
76
|
+
const ioArray = new Float64Array(runTimeLength + inputsLength + outputsLength);
|
|
77
|
+
const rowLength = result.endTime - result.startTime + 1;
|
|
78
|
+
const ioBuffer = ioArray.buffer;
|
|
79
|
+
return Transfer2({ rowLength, ioBuffer }, [ioBuffer]);
|
|
80
|
+
},
|
|
81
|
+
runModel(ioBuffer) {
|
|
82
|
+
if (!wasmModel) {
|
|
83
|
+
throw new Error("WasmModel must be initialized before running the model in worker");
|
|
84
|
+
}
|
|
85
|
+
const runTimeLengthInElements = 1;
|
|
86
|
+
const runTimeLengthInBytes = runTimeLengthInElements * 8;
|
|
87
|
+
const inputsWasmArray = inputsWasmBuffer.getArrayView();
|
|
88
|
+
const inputsLengthInElements = inputsWasmArray.length;
|
|
89
|
+
const inputsLengthInBytes = inputsLengthInElements * 8;
|
|
90
|
+
const inputsBufferArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements);
|
|
91
|
+
inputsWasmArray.set(inputsBufferArray);
|
|
92
|
+
const t0 = perfNow();
|
|
93
|
+
wasmModel.runModel(inputsWasmBuffer, outputsWasmBuffer);
|
|
94
|
+
const elapsed = perfElapsed(t0);
|
|
95
|
+
const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements);
|
|
96
|
+
runTimeBufferArray[0] = elapsed;
|
|
97
|
+
const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes;
|
|
98
|
+
const outputsBufferArray = new Float64Array(ioBuffer, outputsOffsetInBytes);
|
|
99
|
+
outputsBufferArray.set(outputsWasmBuffer.getArrayView());
|
|
100
|
+
return Transfer2(ioBuffer);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
function exposeModelWorker(init) {
|
|
104
|
+
initWasmModel = init;
|
|
105
|
+
expose(modelWorker);
|
|
106
|
+
}
|
|
107
|
+
export {
|
|
108
|
+
exposeModelWorker,
|
|
109
|
+
spawnAsyncModelRunner
|
|
110
|
+
};
|
|
111
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runner.ts","../src/worker.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport { BlobWorker, spawn, Thread, Transfer, Worker } from 'threads'\n\nimport type { ModelRunner } from '@sdeverywhere/runtime'\n\n/**\n * Initialize a `ModelRunner` that runs the model asynchronously in a worker thread.\n *\n * In your app project, define a JavaScript file, called `worker.js` for example, that\n * initializes the model worker in the context of the Web Worker:\n *\n * ```js\n * import { initWasmModelAndBuffers } from '@sdeverywhere/runtime'\n * import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'\n *\n * async function initWasmModel() {\n * const wasmModules = loadWasm()\n * return initWasmModelAndBuffers(...)\n * }\n *\n * exposeModelWorker(initWasmModel)\n * ```\n *\n * Then, in your web app, call the `spawnAsyncModelRunner` function, which\n * will spawn the Web Worker and initialize the `ModelRunner` that communicates\n * with the worker:\n *\n * ```js\n * import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'\n *\n * async function initApp() {\n * // ...\n * const runner = await spawnAsyncModelRunner({ path: './worker.js' })\n * // ...\n * }\n * ```\n *\n * @param workerSpec Either a `path` to the worker JavaScript file, or the `source`\n * containing the full JavaScript source of the worker.\n */\nexport async function spawnAsyncModelRunner(workerSpec: { path: string } | { source: string }): Promise<ModelRunner> {\n if (workerSpec['path']) {\n return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec['path']))\n } else {\n return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec['source']))\n }\n}\n\n/**\n * @hidden For internal use only\n */\nasync function spawnAsyncModelRunnerWithWorker(worker: Worker): Promise<ModelRunner> {\n // Spawn the given Worker that contains the `ModelWorker`\n const modelWorker = await spawn(worker)\n\n // Wait for the worker to initialize the wasm model (in the worker thread)\n const initResult = await modelWorker.initModel()\n const rowLength: number = initResult.rowLength\n let ioBuffer: ArrayBuffer = initResult.ioBuffer\n\n // Use a flag to ensure that only one request is made at a time\n let running = false\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n return {\n runModel: async (inputs, outputs) => {\n if (terminated) {\n throw new Error('Async model runner has already been terminated')\n } else if (running) {\n throw new Error('Async model runner only supports one `runModel` call at a time')\n } else {\n running = true\n }\n\n // The run time is stored in the first 8 bytes\n const runTimeLengthInElements = 1\n const runTimeLengthInBytes = runTimeLengthInElements * 8\n\n // Capture the current set of input values into the reusable buffer\n const inputsLengthInElements = inputs.length\n const inputsLengthInBytes = inputsLengthInElements * 8\n const inputsArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements)\n for (let i = 0; i < inputs.length; i++) {\n inputsArray[i] = inputs[i].get()\n }\n\n // Run the model in the worker. We pass the underlying `ArrayBuffer`\n // instance back to the worker wrapped in a `Transfer` to make it\n // no-copy transferable, and then the worker will return it back\n // to us.\n try {\n ioBuffer = await modelWorker.runModel(Transfer(ioBuffer))\n } finally {\n running = false\n }\n\n // Save the model run time\n const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements)\n outputs.runTimeInMillis = runTimeBufferArray[0]\n\n // Capture the outputs array by copying the data into the given `Outputs`\n // data structure\n const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes\n const outputsArray = new Float64Array(ioBuffer, outputsOffsetInBytes)\n outputs.updateFromBuffer(outputsArray, rowLength)\n\n return outputs\n },\n\n terminate: () => {\n if (terminated) {\n return Promise.resolve()\n } else {\n terminated = true\n return Thread.terminate(modelWorker)\n }\n }\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { TransferDescriptor } from 'threads'\nimport { expose, Transfer } from 'threads/worker'\n\nimport type { WasmBuffer, WasmModel, WasmModelInitResult } from '@sdeverywhere/runtime'\nimport { perfElapsed, perfNow } from '@sdeverywhere/runtime'\n\n/** @hidden */\nlet initWasmModel: () => Promise<WasmModelInitResult>\n/** @hidden */\nlet wasmModel: WasmModel\n/** @hidden */\nlet inputsWasmBuffer: WasmBuffer\n/** @hidden */\nlet outputsWasmBuffer: WasmBuffer\n\ninterface InitResult {\n rowLength: number\n ioBuffer: ArrayBuffer\n}\n\n/** @hidden */\nconst modelWorker = {\n async initModel(): Promise<TransferDescriptor<InitResult>> {\n if (wasmModel) {\n throw new Error('WasmModel was already initialized')\n }\n\n // Initialize the wasm model and associated buffers\n const result = await initWasmModel()\n\n // Capture the `WasmModel` instance and `WasmBuffer` instances\n wasmModel = result.model\n inputsWasmBuffer = result.inputsBuffer\n outputsWasmBuffer = result.outputsBuffer\n\n // Create a combined array that will hold a copy of the inputs and outputs\n // wasm buffers; this buffer is no-copy transferable, whereas the wasm ones\n // are not allowed to be transferred\n const runTimeLength = 8\n const inputsLength = inputsWasmBuffer.getArrayView().length\n const outputsLength = outputsWasmBuffer.getArrayView().length\n const ioArray = new Float64Array(runTimeLength + inputsLength + outputsLength)\n\n // The row length is the number of elements in each row of the outputs buffer\n const rowLength = result.endTime - result.startTime + 1\n\n // Transfer the underlying buffer to the runner\n const ioBuffer = ioArray.buffer\n return Transfer({ rowLength, ioBuffer }, [ioBuffer])\n },\n\n runModel(ioBuffer: ArrayBuffer): TransferDescriptor<ArrayBuffer> {\n if (!wasmModel) {\n throw new Error('WasmModel must be initialized before running the model in worker')\n }\n\n // The run time is stored in the first 8 bytes\n const runTimeLengthInElements = 1\n const runTimeLengthInBytes = runTimeLengthInElements * 8\n\n // Copy the inputs into the wasm inputs buffer\n const inputsWasmArray = inputsWasmBuffer.getArrayView()\n const inputsLengthInElements = inputsWasmArray.length\n const inputsLengthInBytes = inputsLengthInElements * 8\n const inputsBufferArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements)\n inputsWasmArray.set(inputsBufferArray)\n\n // Run the model using the wasm buffers\n const t0 = perfNow()\n wasmModel.runModel(inputsWasmBuffer, outputsWasmBuffer)\n const elapsed = perfElapsed(t0)\n\n // Write the model run time to the buffer\n const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements)\n runTimeBufferArray[0] = elapsed\n\n // Copy the outputs from the wasm outputs buffer\n const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes\n const outputsBufferArray = new Float64Array(ioBuffer, outputsOffsetInBytes)\n outputsBufferArray.set(outputsWasmBuffer.getArrayView())\n\n // Transfer the buffer back to the runner\n return Transfer(ioBuffer)\n }\n}\n\n/**\n * Expose an object in the current worker thread that communicates with the\n * `ModelRunner` instance running in the main thread. The exposed worker\n * object will take care of running the `WasmModel` on the worker thread\n * and sending the outputs back to the main process.\n *\n * @param init The function that initializes the `WasmModel` instance that\n * is used in the worker thread.\n */\nexport function exposeModelWorker(init: () => Promise<WasmModelInitResult>): void {\n // Save the initializer, which will be used when the runner calls `initModel`\n // on the worker\n initWasmModel = init\n\n // Expose the worker implementation to `threads.js`\n expose(modelWorker)\n}\n"],"mappings":"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","names":[]}
|
package/dist/runner.cjs
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/runner.ts
|
|
20
|
+
var runner_exports = {};
|
|
21
|
+
__export(runner_exports, {
|
|
22
|
+
spawnAsyncModelRunner: () => spawnAsyncModelRunner
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(runner_exports);
|
|
25
|
+
var import_threads = require("threads");
|
|
26
|
+
async function spawnAsyncModelRunner(workerSpec) {
|
|
27
|
+
if (workerSpec["path"]) {
|
|
28
|
+
return spawnAsyncModelRunnerWithWorker(new import_threads.Worker(workerSpec["path"]));
|
|
29
|
+
} else {
|
|
30
|
+
return spawnAsyncModelRunnerWithWorker(import_threads.BlobWorker.fromText(workerSpec["source"]));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function spawnAsyncModelRunnerWithWorker(worker) {
|
|
34
|
+
const modelWorker = await (0, import_threads.spawn)(worker);
|
|
35
|
+
const initResult = await modelWorker.initModel();
|
|
36
|
+
const rowLength = initResult.rowLength;
|
|
37
|
+
let ioBuffer = initResult.ioBuffer;
|
|
38
|
+
let running = false;
|
|
39
|
+
let terminated = false;
|
|
40
|
+
return {
|
|
41
|
+
runModel: async (inputs, outputs) => {
|
|
42
|
+
if (terminated) {
|
|
43
|
+
throw new Error("Async model runner has already been terminated");
|
|
44
|
+
} else if (running) {
|
|
45
|
+
throw new Error("Async model runner only supports one `runModel` call at a time");
|
|
46
|
+
} else {
|
|
47
|
+
running = true;
|
|
48
|
+
}
|
|
49
|
+
const runTimeLengthInElements = 1;
|
|
50
|
+
const runTimeLengthInBytes = runTimeLengthInElements * 8;
|
|
51
|
+
const inputsLengthInElements = inputs.length;
|
|
52
|
+
const inputsLengthInBytes = inputsLengthInElements * 8;
|
|
53
|
+
const inputsArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements);
|
|
54
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
55
|
+
inputsArray[i] = inputs[i].get();
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
ioBuffer = await modelWorker.runModel((0, import_threads.Transfer)(ioBuffer));
|
|
59
|
+
} finally {
|
|
60
|
+
running = false;
|
|
61
|
+
}
|
|
62
|
+
const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements);
|
|
63
|
+
outputs.runTimeInMillis = runTimeBufferArray[0];
|
|
64
|
+
const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes;
|
|
65
|
+
const outputsArray = new Float64Array(ioBuffer, outputsOffsetInBytes);
|
|
66
|
+
outputs.updateFromBuffer(outputsArray, rowLength);
|
|
67
|
+
return outputs;
|
|
68
|
+
},
|
|
69
|
+
terminate: () => {
|
|
70
|
+
if (terminated) {
|
|
71
|
+
return Promise.resolve();
|
|
72
|
+
} else {
|
|
73
|
+
terminated = true;
|
|
74
|
+
return import_threads.Thread.terminate(modelWorker);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
80
|
+
0 && (module.exports = {
|
|
81
|
+
spawnAsyncModelRunner
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=runner.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runner.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport { BlobWorker, spawn, Thread, Transfer, Worker } from 'threads'\n\nimport type { ModelRunner } from '@sdeverywhere/runtime'\n\n/**\n * Initialize a `ModelRunner` that runs the model asynchronously in a worker thread.\n *\n * In your app project, define a JavaScript file, called `worker.js` for example, that\n * initializes the model worker in the context of the Web Worker:\n *\n * ```js\n * import { initWasmModelAndBuffers } from '@sdeverywhere/runtime'\n * import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'\n *\n * async function initWasmModel() {\n * const wasmModules = loadWasm()\n * return initWasmModelAndBuffers(...)\n * }\n *\n * exposeModelWorker(initWasmModel)\n * ```\n *\n * Then, in your web app, call the `spawnAsyncModelRunner` function, which\n * will spawn the Web Worker and initialize the `ModelRunner` that communicates\n * with the worker:\n *\n * ```js\n * import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'\n *\n * async function initApp() {\n * // ...\n * const runner = await spawnAsyncModelRunner({ path: './worker.js' })\n * // ...\n * }\n * ```\n *\n * @param workerSpec Either a `path` to the worker JavaScript file, or the `source`\n * containing the full JavaScript source of the worker.\n */\nexport async function spawnAsyncModelRunner(workerSpec: { path: string } | { source: string }): Promise<ModelRunner> {\n if (workerSpec['path']) {\n return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec['path']))\n } else {\n return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec['source']))\n }\n}\n\n/**\n * @hidden For internal use only\n */\nasync function spawnAsyncModelRunnerWithWorker(worker: Worker): Promise<ModelRunner> {\n // Spawn the given Worker that contains the `ModelWorker`\n const modelWorker = await spawn(worker)\n\n // Wait for the worker to initialize the wasm model (in the worker thread)\n const initResult = await modelWorker.initModel()\n const rowLength: number = initResult.rowLength\n let ioBuffer: ArrayBuffer = initResult.ioBuffer\n\n // Use a flag to ensure that only one request is made at a time\n let running = false\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n return {\n runModel: async (inputs, outputs) => {\n if (terminated) {\n throw new Error('Async model runner has already been terminated')\n } else if (running) {\n throw new Error('Async model runner only supports one `runModel` call at a time')\n } else {\n running = true\n }\n\n // The run time is stored in the first 8 bytes\n const runTimeLengthInElements = 1\n const runTimeLengthInBytes = runTimeLengthInElements * 8\n\n // Capture the current set of input values into the reusable buffer\n const inputsLengthInElements = inputs.length\n const inputsLengthInBytes = inputsLengthInElements * 8\n const inputsArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements)\n for (let i = 0; i < inputs.length; i++) {\n inputsArray[i] = inputs[i].get()\n }\n\n // Run the model in the worker. We pass the underlying `ArrayBuffer`\n // instance back to the worker wrapped in a `Transfer` to make it\n // no-copy transferable, and then the worker will return it back\n // to us.\n try {\n ioBuffer = await modelWorker.runModel(Transfer(ioBuffer))\n } finally {\n running = false\n }\n\n // Save the model run time\n const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements)\n outputs.runTimeInMillis = runTimeBufferArray[0]\n\n // Capture the outputs array by copying the data into the given `Outputs`\n // data structure\n const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes\n const outputsArray = new Float64Array(ioBuffer, outputsOffsetInBytes)\n outputs.updateFromBuffer(outputsArray, rowLength)\n\n return outputs\n },\n\n terminate: () => {\n if (terminated) {\n return Promise.resolve()\n } else {\n terminated = true\n return Thread.terminate(modelWorker)\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,qBAA4D;AAuC5D,qCAA4C,YAAyE;AACnH,MAAI,WAAW,SAAS;AACtB,WAAO,gCAAgC,IAAI,sBAAO,WAAW,OAAO,CAAC;AAAA,EACvE,OAAO;AACL,WAAO,gCAAgC,0BAAW,SAAS,WAAW,SAAS,CAAC;AAAA,EAClF;AACF;AAKA,+CAA+C,QAAsC;AAEnF,QAAM,cAAc,MAAM,0BAAM,MAAM;AAGtC,QAAM,aAAa,MAAM,YAAY,UAAU;AAC/C,QAAM,YAAoB,WAAW;AACrC,MAAI,WAAwB,WAAW;AAGvC,MAAI,UAAU;AAGd,MAAI,aAAa;AAEjB,SAAO;AAAA,IACL,UAAU,OAAO,QAAQ,YAAY;AACnC,UAAI,YAAY;AACd,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE,WAAW,SAAS;AAClB,cAAM,IAAI,MAAM,gEAAgE;AAAA,MAClF,OAAO;AACL,kBAAU;AAAA,MACZ;AAGA,YAAM,0BAA0B;AAChC,YAAM,uBAAuB,0BAA0B;AAGvD,YAAM,yBAAyB,OAAO;AACtC,YAAM,sBAAsB,yBAAyB;AACrD,YAAM,cAAc,IAAI,aAAa,UAAU,sBAAsB,sBAAsB;AAC3F,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,oBAAY,KAAK,OAAO,GAAG,IAAI;AAAA,MACjC;AAMA,UAAI;AACF,mBAAW,MAAM,YAAY,SAAS,6BAAS,QAAQ,CAAC;AAAA,MAC1D,UAAE;AACA,kBAAU;AAAA,MACZ;AAGA,YAAM,qBAAqB,IAAI,aAAa,UAAU,GAAG,uBAAuB;AAChF,cAAQ,kBAAkB,mBAAmB;AAI7C,YAAM,uBAAuB,uBAAuB;AACpD,YAAM,eAAe,IAAI,aAAa,UAAU,oBAAoB;AACpE,cAAQ,iBAAiB,cAAc,SAAS;AAEhD,aAAO;AAAA,IACT;AAAA,IAEA,WAAW,MAAM;AACf,UAAI,YAAY;AACd,eAAO,QAAQ,QAAQ;AAAA,MACzB,OAAO;AACL,qBAAa;AACb,eAAO,sBAAO,UAAU,WAAW;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/runner.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ModelRunner } from '@sdeverywhere/runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Initialize a `ModelRunner` that runs the model asynchronously in a worker thread.
|
|
5
|
+
*
|
|
6
|
+
* In your app project, define a JavaScript file, called `worker.js` for example, that
|
|
7
|
+
* initializes the model worker in the context of the Web Worker:
|
|
8
|
+
*
|
|
9
|
+
* ```js
|
|
10
|
+
* import { initWasmModelAndBuffers } from '@sdeverywhere/runtime'
|
|
11
|
+
* import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'
|
|
12
|
+
*
|
|
13
|
+
* async function initWasmModel() {
|
|
14
|
+
* const wasmModules = loadWasm()
|
|
15
|
+
* return initWasmModelAndBuffers(...)
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* exposeModelWorker(initWasmModel)
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Then, in your web app, call the `spawnAsyncModelRunner` function, which
|
|
22
|
+
* will spawn the Web Worker and initialize the `ModelRunner` that communicates
|
|
23
|
+
* with the worker:
|
|
24
|
+
*
|
|
25
|
+
* ```js
|
|
26
|
+
* import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'
|
|
27
|
+
*
|
|
28
|
+
* async function initApp() {
|
|
29
|
+
* // ...
|
|
30
|
+
* const runner = await spawnAsyncModelRunner({ path: './worker.js' })
|
|
31
|
+
* // ...
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @param workerSpec Either a `path` to the worker JavaScript file, or the `source`
|
|
36
|
+
* containing the full JavaScript source of the worker.
|
|
37
|
+
*/
|
|
38
|
+
declare function spawnAsyncModelRunner(workerSpec: {
|
|
39
|
+
path: string;
|
|
40
|
+
} | {
|
|
41
|
+
source: string;
|
|
42
|
+
}): Promise<ModelRunner>;
|
|
43
|
+
|
|
44
|
+
export { spawnAsyncModelRunner };
|
package/dist/runner.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/runner.ts
|
|
2
|
+
import { BlobWorker, spawn, Thread, Transfer, Worker } from "threads";
|
|
3
|
+
async function spawnAsyncModelRunner(workerSpec) {
|
|
4
|
+
if (workerSpec["path"]) {
|
|
5
|
+
return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec["path"]));
|
|
6
|
+
} else {
|
|
7
|
+
return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec["source"]));
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
async function spawnAsyncModelRunnerWithWorker(worker) {
|
|
11
|
+
const modelWorker = await spawn(worker);
|
|
12
|
+
const initResult = await modelWorker.initModel();
|
|
13
|
+
const rowLength = initResult.rowLength;
|
|
14
|
+
let ioBuffer = initResult.ioBuffer;
|
|
15
|
+
let running = false;
|
|
16
|
+
let terminated = false;
|
|
17
|
+
return {
|
|
18
|
+
runModel: async (inputs, outputs) => {
|
|
19
|
+
if (terminated) {
|
|
20
|
+
throw new Error("Async model runner has already been terminated");
|
|
21
|
+
} else if (running) {
|
|
22
|
+
throw new Error("Async model runner only supports one `runModel` call at a time");
|
|
23
|
+
} else {
|
|
24
|
+
running = true;
|
|
25
|
+
}
|
|
26
|
+
const runTimeLengthInElements = 1;
|
|
27
|
+
const runTimeLengthInBytes = runTimeLengthInElements * 8;
|
|
28
|
+
const inputsLengthInElements = inputs.length;
|
|
29
|
+
const inputsLengthInBytes = inputsLengthInElements * 8;
|
|
30
|
+
const inputsArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements);
|
|
31
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
32
|
+
inputsArray[i] = inputs[i].get();
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
ioBuffer = await modelWorker.runModel(Transfer(ioBuffer));
|
|
36
|
+
} finally {
|
|
37
|
+
running = false;
|
|
38
|
+
}
|
|
39
|
+
const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements);
|
|
40
|
+
outputs.runTimeInMillis = runTimeBufferArray[0];
|
|
41
|
+
const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes;
|
|
42
|
+
const outputsArray = new Float64Array(ioBuffer, outputsOffsetInBytes);
|
|
43
|
+
outputs.updateFromBuffer(outputsArray, rowLength);
|
|
44
|
+
return outputs;
|
|
45
|
+
},
|
|
46
|
+
terminate: () => {
|
|
47
|
+
if (terminated) {
|
|
48
|
+
return Promise.resolve();
|
|
49
|
+
} else {
|
|
50
|
+
terminated = true;
|
|
51
|
+
return Thread.terminate(modelWorker);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
spawnAsyncModelRunner
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runner.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport { BlobWorker, spawn, Thread, Transfer, Worker } from 'threads'\n\nimport type { ModelRunner } from '@sdeverywhere/runtime'\n\n/**\n * Initialize a `ModelRunner` that runs the model asynchronously in a worker thread.\n *\n * In your app project, define a JavaScript file, called `worker.js` for example, that\n * initializes the model worker in the context of the Web Worker:\n *\n * ```js\n * import { initWasmModelAndBuffers } from '@sdeverywhere/runtime'\n * import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'\n *\n * async function initWasmModel() {\n * const wasmModules = loadWasm()\n * return initWasmModelAndBuffers(...)\n * }\n *\n * exposeModelWorker(initWasmModel)\n * ```\n *\n * Then, in your web app, call the `spawnAsyncModelRunner` function, which\n * will spawn the Web Worker and initialize the `ModelRunner` that communicates\n * with the worker:\n *\n * ```js\n * import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'\n *\n * async function initApp() {\n * // ...\n * const runner = await spawnAsyncModelRunner({ path: './worker.js' })\n * // ...\n * }\n * ```\n *\n * @param workerSpec Either a `path` to the worker JavaScript file, or the `source`\n * containing the full JavaScript source of the worker.\n */\nexport async function spawnAsyncModelRunner(workerSpec: { path: string } | { source: string }): Promise<ModelRunner> {\n if (workerSpec['path']) {\n return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec['path']))\n } else {\n return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec['source']))\n }\n}\n\n/**\n * @hidden For internal use only\n */\nasync function spawnAsyncModelRunnerWithWorker(worker: Worker): Promise<ModelRunner> {\n // Spawn the given Worker that contains the `ModelWorker`\n const modelWorker = await spawn(worker)\n\n // Wait for the worker to initialize the wasm model (in the worker thread)\n const initResult = await modelWorker.initModel()\n const rowLength: number = initResult.rowLength\n let ioBuffer: ArrayBuffer = initResult.ioBuffer\n\n // Use a flag to ensure that only one request is made at a time\n let running = false\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n return {\n runModel: async (inputs, outputs) => {\n if (terminated) {\n throw new Error('Async model runner has already been terminated')\n } else if (running) {\n throw new Error('Async model runner only supports one `runModel` call at a time')\n } else {\n running = true\n }\n\n // The run time is stored in the first 8 bytes\n const runTimeLengthInElements = 1\n const runTimeLengthInBytes = runTimeLengthInElements * 8\n\n // Capture the current set of input values into the reusable buffer\n const inputsLengthInElements = inputs.length\n const inputsLengthInBytes = inputsLengthInElements * 8\n const inputsArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements)\n for (let i = 0; i < inputs.length; i++) {\n inputsArray[i] = inputs[i].get()\n }\n\n // Run the model in the worker. We pass the underlying `ArrayBuffer`\n // instance back to the worker wrapped in a `Transfer` to make it\n // no-copy transferable, and then the worker will return it back\n // to us.\n try {\n ioBuffer = await modelWorker.runModel(Transfer(ioBuffer))\n } finally {\n running = false\n }\n\n // Save the model run time\n const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements)\n outputs.runTimeInMillis = runTimeBufferArray[0]\n\n // Capture the outputs array by copying the data into the given `Outputs`\n // data structure\n const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes\n const outputsArray = new Float64Array(ioBuffer, outputsOffsetInBytes)\n outputs.updateFromBuffer(outputsArray, rowLength)\n\n return outputs\n },\n\n terminate: () => {\n if (terminated) {\n return Promise.resolve()\n } else {\n terminated = true\n return Thread.terminate(modelWorker)\n }\n }\n }\n}\n"],"mappings":";AAEA;AAuCA,qCAA4C,YAAyE;AACnH,MAAI,WAAW,SAAS;AACtB,WAAO,gCAAgC,IAAI,OAAO,WAAW,OAAO,CAAC;AAAA,EACvE,OAAO;AACL,WAAO,gCAAgC,WAAW,SAAS,WAAW,SAAS,CAAC;AAAA,EAClF;AACF;AAKA,+CAA+C,QAAsC;AAEnF,QAAM,cAAc,MAAM,MAAM,MAAM;AAGtC,QAAM,aAAa,MAAM,YAAY,UAAU;AAC/C,QAAM,YAAoB,WAAW;AACrC,MAAI,WAAwB,WAAW;AAGvC,MAAI,UAAU;AAGd,MAAI,aAAa;AAEjB,SAAO;AAAA,IACL,UAAU,OAAO,QAAQ,YAAY;AACnC,UAAI,YAAY;AACd,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE,WAAW,SAAS;AAClB,cAAM,IAAI,MAAM,gEAAgE;AAAA,MAClF,OAAO;AACL,kBAAU;AAAA,MACZ;AAGA,YAAM,0BAA0B;AAChC,YAAM,uBAAuB,0BAA0B;AAGvD,YAAM,yBAAyB,OAAO;AACtC,YAAM,sBAAsB,yBAAyB;AACrD,YAAM,cAAc,IAAI,aAAa,UAAU,sBAAsB,sBAAsB;AAC3F,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,oBAAY,KAAK,OAAO,GAAG,IAAI;AAAA,MACjC;AAMA,UAAI;AACF,mBAAW,MAAM,YAAY,SAAS,SAAS,QAAQ,CAAC;AAAA,MAC1D,UAAE;AACA,kBAAU;AAAA,MACZ;AAGA,YAAM,qBAAqB,IAAI,aAAa,UAAU,GAAG,uBAAuB;AAChF,cAAQ,kBAAkB,mBAAmB;AAI7C,YAAM,uBAAuB,uBAAuB;AACpD,YAAM,eAAe,IAAI,aAAa,UAAU,oBAAoB;AACpE,cAAQ,iBAAiB,cAAc,SAAS;AAEhD,aAAO;AAAA,IACT;AAAA,IAEA,WAAW,MAAM;AACf,UAAI,YAAY;AACd,eAAO,QAAQ,QAAQ;AAAA,MACzB,OAAO;AACL,qBAAa;AACb,eAAO,OAAO,UAAU,WAAW;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/worker.cjs
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/worker.ts
|
|
20
|
+
var worker_exports = {};
|
|
21
|
+
__export(worker_exports, {
|
|
22
|
+
exposeModelWorker: () => exposeModelWorker
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(worker_exports);
|
|
25
|
+
var import_worker = require("threads/worker");
|
|
26
|
+
var import_runtime = require("@sdeverywhere/runtime");
|
|
27
|
+
var initWasmModel;
|
|
28
|
+
var wasmModel;
|
|
29
|
+
var inputsWasmBuffer;
|
|
30
|
+
var outputsWasmBuffer;
|
|
31
|
+
var modelWorker = {
|
|
32
|
+
async initModel() {
|
|
33
|
+
if (wasmModel) {
|
|
34
|
+
throw new Error("WasmModel was already initialized");
|
|
35
|
+
}
|
|
36
|
+
const result = await initWasmModel();
|
|
37
|
+
wasmModel = result.model;
|
|
38
|
+
inputsWasmBuffer = result.inputsBuffer;
|
|
39
|
+
outputsWasmBuffer = result.outputsBuffer;
|
|
40
|
+
const runTimeLength = 8;
|
|
41
|
+
const inputsLength = inputsWasmBuffer.getArrayView().length;
|
|
42
|
+
const outputsLength = outputsWasmBuffer.getArrayView().length;
|
|
43
|
+
const ioArray = new Float64Array(runTimeLength + inputsLength + outputsLength);
|
|
44
|
+
const rowLength = result.endTime - result.startTime + 1;
|
|
45
|
+
const ioBuffer = ioArray.buffer;
|
|
46
|
+
return (0, import_worker.Transfer)({ rowLength, ioBuffer }, [ioBuffer]);
|
|
47
|
+
},
|
|
48
|
+
runModel(ioBuffer) {
|
|
49
|
+
if (!wasmModel) {
|
|
50
|
+
throw new Error("WasmModel must be initialized before running the model in worker");
|
|
51
|
+
}
|
|
52
|
+
const runTimeLengthInElements = 1;
|
|
53
|
+
const runTimeLengthInBytes = runTimeLengthInElements * 8;
|
|
54
|
+
const inputsWasmArray = inputsWasmBuffer.getArrayView();
|
|
55
|
+
const inputsLengthInElements = inputsWasmArray.length;
|
|
56
|
+
const inputsLengthInBytes = inputsLengthInElements * 8;
|
|
57
|
+
const inputsBufferArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements);
|
|
58
|
+
inputsWasmArray.set(inputsBufferArray);
|
|
59
|
+
const t0 = (0, import_runtime.perfNow)();
|
|
60
|
+
wasmModel.runModel(inputsWasmBuffer, outputsWasmBuffer);
|
|
61
|
+
const elapsed = (0, import_runtime.perfElapsed)(t0);
|
|
62
|
+
const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements);
|
|
63
|
+
runTimeBufferArray[0] = elapsed;
|
|
64
|
+
const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes;
|
|
65
|
+
const outputsBufferArray = new Float64Array(ioBuffer, outputsOffsetInBytes);
|
|
66
|
+
outputsBufferArray.set(outputsWasmBuffer.getArrayView());
|
|
67
|
+
return (0, import_worker.Transfer)(ioBuffer);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
function exposeModelWorker(init) {
|
|
71
|
+
initWasmModel = init;
|
|
72
|
+
(0, import_worker.expose)(modelWorker);
|
|
73
|
+
}
|
|
74
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
75
|
+
0 && (module.exports = {
|
|
76
|
+
exposeModelWorker
|
|
77
|
+
});
|
|
78
|
+
//# sourceMappingURL=worker.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { TransferDescriptor } from 'threads'\nimport { expose, Transfer } from 'threads/worker'\n\nimport type { WasmBuffer, WasmModel, WasmModelInitResult } from '@sdeverywhere/runtime'\nimport { perfElapsed, perfNow } from '@sdeverywhere/runtime'\n\n/** @hidden */\nlet initWasmModel: () => Promise<WasmModelInitResult>\n/** @hidden */\nlet wasmModel: WasmModel\n/** @hidden */\nlet inputsWasmBuffer: WasmBuffer\n/** @hidden */\nlet outputsWasmBuffer: WasmBuffer\n\ninterface InitResult {\n rowLength: number\n ioBuffer: ArrayBuffer\n}\n\n/** @hidden */\nconst modelWorker = {\n async initModel(): Promise<TransferDescriptor<InitResult>> {\n if (wasmModel) {\n throw new Error('WasmModel was already initialized')\n }\n\n // Initialize the wasm model and associated buffers\n const result = await initWasmModel()\n\n // Capture the `WasmModel` instance and `WasmBuffer` instances\n wasmModel = result.model\n inputsWasmBuffer = result.inputsBuffer\n outputsWasmBuffer = result.outputsBuffer\n\n // Create a combined array that will hold a copy of the inputs and outputs\n // wasm buffers; this buffer is no-copy transferable, whereas the wasm ones\n // are not allowed to be transferred\n const runTimeLength = 8\n const inputsLength = inputsWasmBuffer.getArrayView().length\n const outputsLength = outputsWasmBuffer.getArrayView().length\n const ioArray = new Float64Array(runTimeLength + inputsLength + outputsLength)\n\n // The row length is the number of elements in each row of the outputs buffer\n const rowLength = result.endTime - result.startTime + 1\n\n // Transfer the underlying buffer to the runner\n const ioBuffer = ioArray.buffer\n return Transfer({ rowLength, ioBuffer }, [ioBuffer])\n },\n\n runModel(ioBuffer: ArrayBuffer): TransferDescriptor<ArrayBuffer> {\n if (!wasmModel) {\n throw new Error('WasmModel must be initialized before running the model in worker')\n }\n\n // The run time is stored in the first 8 bytes\n const runTimeLengthInElements = 1\n const runTimeLengthInBytes = runTimeLengthInElements * 8\n\n // Copy the inputs into the wasm inputs buffer\n const inputsWasmArray = inputsWasmBuffer.getArrayView()\n const inputsLengthInElements = inputsWasmArray.length\n const inputsLengthInBytes = inputsLengthInElements * 8\n const inputsBufferArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements)\n inputsWasmArray.set(inputsBufferArray)\n\n // Run the model using the wasm buffers\n const t0 = perfNow()\n wasmModel.runModel(inputsWasmBuffer, outputsWasmBuffer)\n const elapsed = perfElapsed(t0)\n\n // Write the model run time to the buffer\n const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements)\n runTimeBufferArray[0] = elapsed\n\n // Copy the outputs from the wasm outputs buffer\n const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes\n const outputsBufferArray = new Float64Array(ioBuffer, outputsOffsetInBytes)\n outputsBufferArray.set(outputsWasmBuffer.getArrayView())\n\n // Transfer the buffer back to the runner\n return Transfer(ioBuffer)\n }\n}\n\n/**\n * Expose an object in the current worker thread that communicates with the\n * `ModelRunner` instance running in the main thread. The exposed worker\n * object will take care of running the `WasmModel` on the worker thread\n * and sending the outputs back to the main process.\n *\n * @param init The function that initializes the `WasmModel` instance that\n * is used in the worker thread.\n */\nexport function exposeModelWorker(init: () => Promise<WasmModelInitResult>): void {\n // Save the initializer, which will be used when the runner calls `initModel`\n // on the worker\n initWasmModel = init\n\n // Expose the worker implementation to `threads.js`\n expose(modelWorker)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,oBAAiC;AAGjC,qBAAqC;AAGrC,IAAI;AAEJ,IAAI;AAEJ,IAAI;AAEJ,IAAI;AAQJ,IAAM,cAAc;AAAA,EAClB,MAAM,YAAqD;AACzD,QAAI,WAAW;AACb,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AAGA,UAAM,SAAS,MAAM,cAAc;AAGnC,gBAAY,OAAO;AACnB,uBAAmB,OAAO;AAC1B,wBAAoB,OAAO;AAK3B,UAAM,gBAAgB;AACtB,UAAM,eAAe,iBAAiB,aAAa,EAAE;AACrD,UAAM,gBAAgB,kBAAkB,aAAa,EAAE;AACvD,UAAM,UAAU,IAAI,aAAa,gBAAgB,eAAe,aAAa;AAG7E,UAAM,YAAY,OAAO,UAAU,OAAO,YAAY;AAGtD,UAAM,WAAW,QAAQ;AACzB,WAAO,4BAAS,EAAE,WAAW,SAAS,GAAG,CAAC,QAAQ,CAAC;AAAA,EACrD;AAAA,EAEA,SAAS,UAAwD;AAC/D,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,kEAAkE;AAAA,IACpF;AAGA,UAAM,0BAA0B;AAChC,UAAM,uBAAuB,0BAA0B;AAGvD,UAAM,kBAAkB,iBAAiB,aAAa;AACtD,UAAM,yBAAyB,gBAAgB;AAC/C,UAAM,sBAAsB,yBAAyB;AACrD,UAAM,oBAAoB,IAAI,aAAa,UAAU,sBAAsB,sBAAsB;AACjG,oBAAgB,IAAI,iBAAiB;AAGrC,UAAM,KAAK,4BAAQ;AACnB,cAAU,SAAS,kBAAkB,iBAAiB;AACtD,UAAM,UAAU,gCAAY,EAAE;AAG9B,UAAM,qBAAqB,IAAI,aAAa,UAAU,GAAG,uBAAuB;AAChF,uBAAmB,KAAK;AAGxB,UAAM,uBAAuB,uBAAuB;AACpD,UAAM,qBAAqB,IAAI,aAAa,UAAU,oBAAoB;AAC1E,uBAAmB,IAAI,kBAAkB,aAAa,CAAC;AAGvD,WAAO,4BAAS,QAAQ;AAAA,EAC1B;AACF;AAWO,2BAA2B,MAAgD;AAGhF,kBAAgB;AAGhB,4BAAO,WAAW;AACpB;","names":[]}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { WasmModelInitResult } from '@sdeverywhere/runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Expose an object in the current worker thread that communicates with the
|
|
5
|
+
* `ModelRunner` instance running in the main thread. The exposed worker
|
|
6
|
+
* object will take care of running the `WasmModel` on the worker thread
|
|
7
|
+
* and sending the outputs back to the main process.
|
|
8
|
+
*
|
|
9
|
+
* @param init The function that initializes the `WasmModel` instance that
|
|
10
|
+
* is used in the worker thread.
|
|
11
|
+
*/
|
|
12
|
+
declare function exposeModelWorker(init: () => Promise<WasmModelInitResult>): void;
|
|
13
|
+
|
|
14
|
+
export { exposeModelWorker };
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/worker.ts
|
|
2
|
+
import { expose, Transfer } from "threads/worker";
|
|
3
|
+
import { perfElapsed, perfNow } from "@sdeverywhere/runtime";
|
|
4
|
+
var initWasmModel;
|
|
5
|
+
var wasmModel;
|
|
6
|
+
var inputsWasmBuffer;
|
|
7
|
+
var outputsWasmBuffer;
|
|
8
|
+
var modelWorker = {
|
|
9
|
+
async initModel() {
|
|
10
|
+
if (wasmModel) {
|
|
11
|
+
throw new Error("WasmModel was already initialized");
|
|
12
|
+
}
|
|
13
|
+
const result = await initWasmModel();
|
|
14
|
+
wasmModel = result.model;
|
|
15
|
+
inputsWasmBuffer = result.inputsBuffer;
|
|
16
|
+
outputsWasmBuffer = result.outputsBuffer;
|
|
17
|
+
const runTimeLength = 8;
|
|
18
|
+
const inputsLength = inputsWasmBuffer.getArrayView().length;
|
|
19
|
+
const outputsLength = outputsWasmBuffer.getArrayView().length;
|
|
20
|
+
const ioArray = new Float64Array(runTimeLength + inputsLength + outputsLength);
|
|
21
|
+
const rowLength = result.endTime - result.startTime + 1;
|
|
22
|
+
const ioBuffer = ioArray.buffer;
|
|
23
|
+
return Transfer({ rowLength, ioBuffer }, [ioBuffer]);
|
|
24
|
+
},
|
|
25
|
+
runModel(ioBuffer) {
|
|
26
|
+
if (!wasmModel) {
|
|
27
|
+
throw new Error("WasmModel must be initialized before running the model in worker");
|
|
28
|
+
}
|
|
29
|
+
const runTimeLengthInElements = 1;
|
|
30
|
+
const runTimeLengthInBytes = runTimeLengthInElements * 8;
|
|
31
|
+
const inputsWasmArray = inputsWasmBuffer.getArrayView();
|
|
32
|
+
const inputsLengthInElements = inputsWasmArray.length;
|
|
33
|
+
const inputsLengthInBytes = inputsLengthInElements * 8;
|
|
34
|
+
const inputsBufferArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements);
|
|
35
|
+
inputsWasmArray.set(inputsBufferArray);
|
|
36
|
+
const t0 = perfNow();
|
|
37
|
+
wasmModel.runModel(inputsWasmBuffer, outputsWasmBuffer);
|
|
38
|
+
const elapsed = perfElapsed(t0);
|
|
39
|
+
const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements);
|
|
40
|
+
runTimeBufferArray[0] = elapsed;
|
|
41
|
+
const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes;
|
|
42
|
+
const outputsBufferArray = new Float64Array(ioBuffer, outputsOffsetInBytes);
|
|
43
|
+
outputsBufferArray.set(outputsWasmBuffer.getArrayView());
|
|
44
|
+
return Transfer(ioBuffer);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
function exposeModelWorker(init) {
|
|
48
|
+
initWasmModel = init;
|
|
49
|
+
expose(modelWorker);
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
exposeModelWorker
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { TransferDescriptor } from 'threads'\nimport { expose, Transfer } from 'threads/worker'\n\nimport type { WasmBuffer, WasmModel, WasmModelInitResult } from '@sdeverywhere/runtime'\nimport { perfElapsed, perfNow } from '@sdeverywhere/runtime'\n\n/** @hidden */\nlet initWasmModel: () => Promise<WasmModelInitResult>\n/** @hidden */\nlet wasmModel: WasmModel\n/** @hidden */\nlet inputsWasmBuffer: WasmBuffer\n/** @hidden */\nlet outputsWasmBuffer: WasmBuffer\n\ninterface InitResult {\n rowLength: number\n ioBuffer: ArrayBuffer\n}\n\n/** @hidden */\nconst modelWorker = {\n async initModel(): Promise<TransferDescriptor<InitResult>> {\n if (wasmModel) {\n throw new Error('WasmModel was already initialized')\n }\n\n // Initialize the wasm model and associated buffers\n const result = await initWasmModel()\n\n // Capture the `WasmModel` instance and `WasmBuffer` instances\n wasmModel = result.model\n inputsWasmBuffer = result.inputsBuffer\n outputsWasmBuffer = result.outputsBuffer\n\n // Create a combined array that will hold a copy of the inputs and outputs\n // wasm buffers; this buffer is no-copy transferable, whereas the wasm ones\n // are not allowed to be transferred\n const runTimeLength = 8\n const inputsLength = inputsWasmBuffer.getArrayView().length\n const outputsLength = outputsWasmBuffer.getArrayView().length\n const ioArray = new Float64Array(runTimeLength + inputsLength + outputsLength)\n\n // The row length is the number of elements in each row of the outputs buffer\n const rowLength = result.endTime - result.startTime + 1\n\n // Transfer the underlying buffer to the runner\n const ioBuffer = ioArray.buffer\n return Transfer({ rowLength, ioBuffer }, [ioBuffer])\n },\n\n runModel(ioBuffer: ArrayBuffer): TransferDescriptor<ArrayBuffer> {\n if (!wasmModel) {\n throw new Error('WasmModel must be initialized before running the model in worker')\n }\n\n // The run time is stored in the first 8 bytes\n const runTimeLengthInElements = 1\n const runTimeLengthInBytes = runTimeLengthInElements * 8\n\n // Copy the inputs into the wasm inputs buffer\n const inputsWasmArray = inputsWasmBuffer.getArrayView()\n const inputsLengthInElements = inputsWasmArray.length\n const inputsLengthInBytes = inputsLengthInElements * 8\n const inputsBufferArray = new Float64Array(ioBuffer, runTimeLengthInBytes, inputsLengthInElements)\n inputsWasmArray.set(inputsBufferArray)\n\n // Run the model using the wasm buffers\n const t0 = perfNow()\n wasmModel.runModel(inputsWasmBuffer, outputsWasmBuffer)\n const elapsed = perfElapsed(t0)\n\n // Write the model run time to the buffer\n const runTimeBufferArray = new Float64Array(ioBuffer, 0, runTimeLengthInElements)\n runTimeBufferArray[0] = elapsed\n\n // Copy the outputs from the wasm outputs buffer\n const outputsOffsetInBytes = runTimeLengthInBytes + inputsLengthInBytes\n const outputsBufferArray = new Float64Array(ioBuffer, outputsOffsetInBytes)\n outputsBufferArray.set(outputsWasmBuffer.getArrayView())\n\n // Transfer the buffer back to the runner\n return Transfer(ioBuffer)\n }\n}\n\n/**\n * Expose an object in the current worker thread that communicates with the\n * `ModelRunner` instance running in the main thread. The exposed worker\n * object will take care of running the `WasmModel` on the worker thread\n * and sending the outputs back to the main process.\n *\n * @param init The function that initializes the `WasmModel` instance that\n * is used in the worker thread.\n */\nexport function exposeModelWorker(init: () => Promise<WasmModelInitResult>): void {\n // Save the initializer, which will be used when the runner calls `initModel`\n // on the worker\n initWasmModel = init\n\n // Expose the worker implementation to `threads.js`\n expose(modelWorker)\n}\n"],"mappings":";AAGA;AAGA;AAGA,IAAI;AAEJ,IAAI;AAEJ,IAAI;AAEJ,IAAI;AAQJ,IAAM,cAAc;AAAA,EAClB,MAAM,YAAqD;AACzD,QAAI,WAAW;AACb,YAAM,IAAI,MAAM,mCAAmC;AAAA,IACrD;AAGA,UAAM,SAAS,MAAM,cAAc;AAGnC,gBAAY,OAAO;AACnB,uBAAmB,OAAO;AAC1B,wBAAoB,OAAO;AAK3B,UAAM,gBAAgB;AACtB,UAAM,eAAe,iBAAiB,aAAa,EAAE;AACrD,UAAM,gBAAgB,kBAAkB,aAAa,EAAE;AACvD,UAAM,UAAU,IAAI,aAAa,gBAAgB,eAAe,aAAa;AAG7E,UAAM,YAAY,OAAO,UAAU,OAAO,YAAY;AAGtD,UAAM,WAAW,QAAQ;AACzB,WAAO,SAAS,EAAE,WAAW,SAAS,GAAG,CAAC,QAAQ,CAAC;AAAA,EACrD;AAAA,EAEA,SAAS,UAAwD;AAC/D,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,MAAM,kEAAkE;AAAA,IACpF;AAGA,UAAM,0BAA0B;AAChC,UAAM,uBAAuB,0BAA0B;AAGvD,UAAM,kBAAkB,iBAAiB,aAAa;AACtD,UAAM,yBAAyB,gBAAgB;AAC/C,UAAM,sBAAsB,yBAAyB;AACrD,UAAM,oBAAoB,IAAI,aAAa,UAAU,sBAAsB,sBAAsB;AACjG,oBAAgB,IAAI,iBAAiB;AAGrC,UAAM,KAAK,QAAQ;AACnB,cAAU,SAAS,kBAAkB,iBAAiB;AACtD,UAAM,UAAU,YAAY,EAAE;AAG9B,UAAM,qBAAqB,IAAI,aAAa,UAAU,GAAG,uBAAuB;AAChF,uBAAmB,KAAK;AAGxB,UAAM,uBAAuB,uBAAuB;AACpD,UAAM,qBAAqB,IAAI,aAAa,UAAU,oBAAoB;AAC1E,uBAAmB,IAAI,kBAAkB,aAAa,CAAC;AAGvD,WAAO,SAAS,QAAQ;AAAA,EAC1B;AACF;AAWO,2BAA2B,MAAgD;AAGhF,kBAAgB;AAGhB,SAAO,WAAW;AACpB;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sdeverywhere/runtime-async",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist/**"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
},
|
|
17
|
+
"./runner": {
|
|
18
|
+
"types": "./dist/runner.d.ts",
|
|
19
|
+
"import": "./dist/runner.js",
|
|
20
|
+
"require": "./dist/runner.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./worker": {
|
|
23
|
+
"types": "./dist/worker.d.ts",
|
|
24
|
+
"import": "./dist/worker.js",
|
|
25
|
+
"require": "./dist/worker.cjs"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@sdeverywhere/runtime": "^0.1.0",
|
|
30
|
+
"threads": "1.7.0"
|
|
31
|
+
},
|
|
32
|
+
"author": "Climate Interactive",
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"homepage": "https://sdeverywhere.org",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/climateinteractive/SDEverywhere.git",
|
|
38
|
+
"directory": "packages/runtime-async"
|
|
39
|
+
},
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/climateinteractive/SDEverywhere/issues"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"clean": "rm -rf dist",
|
|
45
|
+
"lint": "eslint src --ext .ts --max-warnings 0",
|
|
46
|
+
"prettier:check": "prettier --check .",
|
|
47
|
+
"prettier:fix": "prettier --write .",
|
|
48
|
+
"precommit": "../../scripts/precommit",
|
|
49
|
+
"test": "vitest run",
|
|
50
|
+
"test:watch": "vitest",
|
|
51
|
+
"test:ci": "vitest run",
|
|
52
|
+
"type-check": "tsc --noEmit -p tsconfig-build.json",
|
|
53
|
+
"build": "tsup",
|
|
54
|
+
"docs": "../../scripts/gen-docs.js",
|
|
55
|
+
"ci:build": "run-s clean lint prettier:check type-check build test:ci docs"
|
|
56
|
+
}
|
|
57
|
+
}
|