Estoy usando Onnxruntime en NodeJS para ejecutar modelos convertidos onnx en el backend de la CPU. Ejecuto la inferencia del modelo en paralelo usando 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); ejecutando esta inference de método de instancia de clase que llama a un método estático Util.performance.now
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 el momento es incorrecto y resumido. El objeto de performance parece
Util = { performance: { now: function (start) { if (!start) { return process.hrtime(); } var end = process.hrtime(start); return Math.round((end[0] * 1000) + (end[1] / 1000000)); } } }y se usa de la manera habitual
// this runs parallel const start = Util.performance.now(); // computation const duration = (Util.performance.now() - start).toFixed(1); Ahora, dentro de la diversión de performance , el alcance de las variables de start y end es local, entonces, ¿qué sucede con Promise.allSettled ? Esperaría que el momento fuera correcto debido al alcance local.
La mecánica de tiempo es correcta, pero cuando se llama a session.run , iniciará alguna API asíncrona (no JavaScript) mientras ya devuelve el objeto de promesa pendiente. Esto permitirá que otras ejecuciones de inference también llamen a session.run , lo que lleva a un estado en el que la API asincrónica se ocupa de varias solicitudes de este tipo al mismo tiempo, por lo que se cuentan los mismos intervalos de tiempo en múltiples contextos de ejecución de inference . Estas solicitudes pueden incluso terminar en momentos bastante cercanos. Cuando eso sucede, una tras otra, las ejecuciones de inference se reanudan con el código que finaliza el tiempo (estableciendo sus variables de duration ). Está claro que estas duraciones pueden y probablemente se superpondrán entre sí.
Para visualizarlo para 3 ejecuciones de inference , podrías tener esto:
start-----------------------------start+duration start-----------------------------start+duration start------------------------------start+duration time -----> Si no desea este paralelismo, no debe realizar todas las llamadas de inference casi al mismo tiempo, sino esperar con cada ejecución siguiente hasta que se resuelva la anterior:
for (let seq of sequences) { let value = await self.inference(self.session, self.tokenizer, seq)); // ... } De esta manera, las ejecuciones de la parte asíncrona de session.run no se superpondrán y no sufrirán rendimiento debido a esa concurrencia. Esperaría que los tiempos fueran más como esto:
start---------------start+duration start-------------start+duration start------------start+duration time ----->Ahora los intervalos de tiempo no se contarán más de una vez, aunque la duración total de todo el proceso probablemente sea mayor.
Tenga en cuenta que el comportamiento no tiene nada que ver con Promise.allSettled , porque obtendrá esos resultados de todos modos, incluso si elimina esa llamada de Promise.allSettled de su programa.