performance.now 将 onnxruntime 用于顺序和并行执行模式
performance.now using onnxruntime for sequential and parallel execution modes
我在 NodeJS 中使用 Onnxruntime 在 cpu 后端执行 onnx 转换模型。我 运行 使用 Promise.allSettled:
并行建模推理
var promises = sequences.map(seq => self.inference(self.session, self.tokenizer, seq));
results = (await Promise.allSettled(promises)).filter(p => p.status === "fulfilled").map(p => p.value);
运行调用静态方法 Util.performance.now
的 class 实例方法 inference
ONNX.prototype.inference = async function (session, tokenizer, text) {
const default_labels = this._options.model.default_labels;
const labels = this._options.model.labels;
const debug = this._options.debug;
try {
const encoded_ids = await tokenizer.tokenize(text);
if (encoded_ids.length === 0) {
return [0.0, default_labels];
}
const model_input = ONNX.create_model_input(encoded_ids);
const start = Util.performance.now();
const output = await session.run(model_input, ['output_0']);
const duration = Util.performance.now(start).toFixed(1);
const sequence_length = model_input['input_ids'].size;
if (debug) console.log("latency = " + duration + "ms, sequence_length=" + sequence_length);
const probs = output['output_0'].data.map(ONNX.sigmoid).map(t => Math.floor(t * 100));
const result = [];
for (var i = 0; i < labels.length; i++) {
const t = [labels[i], probs[i]];
result[i] = t;
}
result.sort(ONNX.sortResult);
const result_list = [];
for (i = 0; i < 6; i++) {
result_list[i] = result[i];
}
return [parseFloat(duration), result_list];
} catch (e) {
return [0.0, default_labels];
}
}//inference
时机不对,总结一下。
performance
对象看起来像
Util = {
performance: {
now: function (start) {
if (!start) {
return process.hrtime();
}
var end = process.hrtime(start);
return Math.round((end[0] * 1000) + (end[1] / 1000000));
}
}
}
并且按常规使用
// this runs parallel
const start = Util.performance.now();
// computation
const duration = (Util.performance.now() - start).toFixed(1);
现在,在 performance
fun 中,start
和 end
变量作用域是局部的,那么使用 Promise.allSettled
会发生什么?由于本地范围,我希望时间是正确的。
计时机制是正确的,但是当调用 session.run
时,它将启动一些异步 API (non-JavaScript),同时已经返回挂起的承诺对象。这将允许 inference
的其他执行也调用 session.run
,导致异步 API 正在同时处理多个此类请求的状态,因此 相同 时间片被计算在 inference
的多个执行上下文中。这些请求甚至可能在非常接近的时刻结束。当发生这种情况时,inference
的另一个执行将继续执行结束计时的代码(设置它们的 duration
变量)。很明显,这些持续时间可以而且可能 重叠 彼此。
要将其可视化为 inference
的 3 次执行,您可以这样:
start-----------------------------start+duration
start-----------------------------start+duration
start------------------------------start+duration
time ----->
如果你不想要这种并行性,你不应该几乎同时调用所有 inference
,而是等待每次下一次执行,直到上一次执行:
for (let seq of sequences) {
let value = await self.inference(self.session, self.tokenizer, seq));
// ...
}
这样 session.run
的异步部分的执行不会重叠,也不会因为并发而影响性能。我希望时间安排更像这样:
start---------------start+duration
start-------------start+duration
start------------start+duration
time ----->
现在时间片不会被计算多次,即使整个过程的总持续时间可能会更长。
请注意,该行为与 Promise.allSettled
无关,因为您无论如何都会得到这些输出,即使您从程序中删除 Promise.allSettled
调用也是如此。
我在 NodeJS 中使用 Onnxruntime 在 cpu 后端执行 onnx 转换模型。我 运行 使用 Promise.allSettled:
并行建模推理var promises = sequences.map(seq => self.inference(self.session, self.tokenizer, seq));
results = (await Promise.allSettled(promises)).filter(p => p.status === "fulfilled").map(p => p.value);
运行调用静态方法 Util.performance.now
inference
ONNX.prototype.inference = async function (session, tokenizer, text) {
const default_labels = this._options.model.default_labels;
const labels = this._options.model.labels;
const debug = this._options.debug;
try {
const encoded_ids = await tokenizer.tokenize(text);
if (encoded_ids.length === 0) {
return [0.0, default_labels];
}
const model_input = ONNX.create_model_input(encoded_ids);
const start = Util.performance.now();
const output = await session.run(model_input, ['output_0']);
const duration = Util.performance.now(start).toFixed(1);
const sequence_length = model_input['input_ids'].size;
if (debug) console.log("latency = " + duration + "ms, sequence_length=" + sequence_length);
const probs = output['output_0'].data.map(ONNX.sigmoid).map(t => Math.floor(t * 100));
const result = [];
for (var i = 0; i < labels.length; i++) {
const t = [labels[i], probs[i]];
result[i] = t;
}
result.sort(ONNX.sortResult);
const result_list = [];
for (i = 0; i < 6; i++) {
result_list[i] = result[i];
}
return [parseFloat(duration), result_list];
} catch (e) {
return [0.0, default_labels];
}
}//inference
时机不对,总结一下。
performance
对象看起来像
Util = {
performance: {
now: function (start) {
if (!start) {
return process.hrtime();
}
var end = process.hrtime(start);
return Math.round((end[0] * 1000) + (end[1] / 1000000));
}
}
}
并且按常规使用
// this runs parallel
const start = Util.performance.now();
// computation
const duration = (Util.performance.now() - start).toFixed(1);
现在,在 performance
fun 中,start
和 end
变量作用域是局部的,那么使用 Promise.allSettled
会发生什么?由于本地范围,我希望时间是正确的。
计时机制是正确的,但是当调用 session.run
时,它将启动一些异步 API (non-JavaScript),同时已经返回挂起的承诺对象。这将允许 inference
的其他执行也调用 session.run
,导致异步 API 正在同时处理多个此类请求的状态,因此 相同 时间片被计算在 inference
的多个执行上下文中。这些请求甚至可能在非常接近的时刻结束。当发生这种情况时,inference
的另一个执行将继续执行结束计时的代码(设置它们的 duration
变量)。很明显,这些持续时间可以而且可能 重叠 彼此。
要将其可视化为 inference
的 3 次执行,您可以这样:
start-----------------------------start+duration
start-----------------------------start+duration
start------------------------------start+duration
time ----->
如果你不想要这种并行性,你不应该几乎同时调用所有 inference
,而是等待每次下一次执行,直到上一次执行:
for (let seq of sequences) {
let value = await self.inference(self.session, self.tokenizer, seq));
// ...
}
这样 session.run
的异步部分的执行不会重叠,也不会因为并发而影响性能。我希望时间安排更像这样:
start---------------start+duration
start-------------start+duration
start------------start+duration
time ----->
现在时间片不会被计算多次,即使整个过程的总持续时间可能会更长。
请注意,该行为与 Promise.allSettled
无关,因为您无论如何都会得到这些输出,即使您从程序中删除 Promise.allSettled
调用也是如此。