¿Cómo mido el tiempo de ejecución del código JavaScript con devoluciones de llamada?

319

Tengo un fragmento de código JavaScript que estoy ejecutando usando el node.js intérprete.

for(var i = 1; i < LIMIT; i++) {
  var user = {
    id: i,
    name: "MongoUser [" + i + "]"
  };
  db.users.save(user, function(err, saved) {
    if(err || !saved) {
      console.log("Error");
    } else {
      console.log("Saved");
    }
  });
}

¿Cómo puedo medir el tiempo que tardan estas operaciones de inserción de bases de datos? Podría calcular la diferencia de valores de fecha después y antes de este fragmento de código, pero eso sería incorrecto debido a la naturaleza asincrónica del código.

Sombra tormenta
fuente
8
Simplemente lea la hora de inicio antes de la llamada de db, y la hora de finalización DENTRO de la devolución de llamada ..
BFil
Existe la posibilidad de que el momento en que el DB finalice la inserción y el momento en que se ejecute la devolución de llamada no sea el mismo, ¿y esto introduciría un error en la medición?
Stormshadow
1
No, no debe preocuparse por eso, si el código de la biblioteca db está bien diseñado y no maneja ninguna otra operación antes de disparar la devolución de llamada, debe obtener una buena medida. También puede perfilar la inserción poniendo las marcas de tiempo dentro del código de la biblioteca, donde se lleva a cabo en realidad el inserto, en lugar de su propia, pero, de nuevo, no me preocupe
bFIL
Recomiendo probar NodeTime, que parece ser una buena opción para lo que está intentando hacer.
Julian Knight
Escribí timerlogque es similar console.time()pero con características adicionales; github.com/brillout/timerlog
brillante

Respuestas:

719

Use el Node.js console.time()y console.timeEnd():

var i;
console.time("dbsave");

for(i = 1; i < LIMIT; i++){
    db.users.save({id : i, name : "MongoUser [" + i + "]"}, end);
}

end = function(err, saved) {
    console.log(( err || !saved )?"Error":"Saved");
    if(--i === 1){console.timeEnd("dbsave");}
};
usuario2362662
fuente
31
Solución limpia e integrada para el nodo.
Behlül Uçar
45
> Quiero saber cómo medir el tiempo que tardan estas operaciones de inserción de db. --- console.timeEnd ("dbsave") acaba de salir para consolar el tiempo. No puedes usar eso más y es menos flexible. Si necesita el valor de tiempo real, como en la pregunta original, no puede usar console.timeEnd ("dbsave")
gogaman
@gogaman, este es un buen punto, ya que no puede capturar la salida de console.timeEnd (). ¿Quizás podría ser útil canalizar la salida a un archivo y utilizarla desde allí?
Doug Molineux
55
Entonces, ¿cuál es la diferencia entre console.time () y process.hrtime () en la respuesta a continuación?
amarillo-santo
3
Valdría la pena agregar una nota de que el tiempo de ejecución se imprime, solo para que los nuevos usuarios ahora.
Janko-m
208

Hay un método diseñado para esto. Echa un vistazo a process.hrtime (); .

Entonces, básicamente puse esto en la parte superior de mi aplicación.

var start = process.hrtime();

var elapsed_time = function(note){
    var precision = 3; // 3 decimal places
    var elapsed = process.hrtime(start)[1] / 1000000; // divide by a million to get nano to milli
    console.log(process.hrtime(start)[0] + " s, " + elapsed.toFixed(precision) + " ms - " + note); // print message + time
    start = process.hrtime(); // reset the timer
}

Luego lo uso para ver cuánto duran las funciones. Aquí hay un ejemplo básico que imprime el contenido de un archivo de texto llamado "output.txt":

var debug = true;
http.createServer(function(request, response) {

    if(debug) console.log("----------------------------------");
    if(debug) elapsed_time("recieved request");

    var send_html = function(err, contents) {
        if(debug) elapsed_time("start send_html()");
        response.writeHead(200, {'Content-Type': 'text/html' } );
        response.end(contents);
        if(debug) elapsed_time("end send_html()");
    }

    if(debug) elapsed_time("start readFile()");
    fs.readFile('output.txt', send_html);
    if(debug) elapsed_time("end readFile()");

}).listen(8080);

Aquí hay una prueba rápida que puede ejecutar en una terminal (BASH shell):

for i in {1..100}; do echo $i; curl http://localhost:8080/; done
D.Deriso
fuente
3
es tan superior a la consola. ¿Solución de tiempo de alguna manera?
scravy
31
Sí, es mucho más preciso y puede almacenar el resultado en una variable
Dallas Clark
Este funciona para mí, ya que quería llamar al temporizador varias veces
tbh__
2
¿Por qué llamas process.hrtime(start)dos veces? ¿Hay alguna razón particular para ello?
Sohail Si
1
process.hrtime ([time]), donde time es un parámetro opcional que debe ser el resultado de una llamada previa a process.hrtime () para diferir con la hora actual. Da la diferencia entre la llamada actual y la llamada hrtime anterior.
Nilesh Jain
72

La invocación console.time('label')registrará la hora actual en milisegundos, luego la llamada posterior console.timeEnd('label')mostrará la duración desde ese punto.

El tiempo en milisegundos se imprimirá automáticamente junto a la etiqueta, por lo que no tiene que hacer una llamada por separado a console.log para imprimir una etiqueta:

console.time('test');
//some code
console.timeEnd('test'); //Prints something like that-> test: 11374.004ms

Para obtener más información, consulte los documentos de desarrollador de Mozilla enconsole.time .

jfcorugedo
fuente
¿Qué agrega esto a la respuesta aceptada ?
Dan Dascalescu
1
La respuesta aceptada fue modificada después de mi respuesta para usar mi código
jfcorugedo
24

Sorprendido, nadie había mencionado aún las nuevas bibliotecas integradas:

Disponible en Nodo> = 8.5, y debe estar en Navegadores modernos

https://developer.mozilla.org/en-US/docs/Web/API/Performance

https://nodejs.org/docs/latest-v8.x/api/perf_hooks.html#

Nodo 8.5 ~ 9.x (Firefox, Chrome)

// const { performance } = require('perf_hooks'); // enable for node
const delay = time => new Promise(res=>setTimeout(res,time))
async function doSomeLongRunningProcess(){
  await delay(1000);
}
performance.mark('A');
(async ()=>{
  await doSomeLongRunningProcess();
  performance.mark('B');
  performance.measure('A to B', 'A', 'B');
  const measure = performance.getEntriesByName('A to B')[0];
  // firefox appears to only show second precision.
  console.log(measure.duration);
  performance.clearMeasures(); // apparently you should remove entries...
  // Prints the number of milliseconds between Mark 'A' and Mark 'B'
})();

https://repl.it/@CodyGeisler/NodeJsPerformanceHooks

Nodo 10.x

https://nodejs.org/docs/latest-v10.x/api/perf_hooks.html

const { PerformanceObserver, performance } = require('perf_hooks');
const delay = time => new Promise(res => setTimeout(res, time))
async function doSomeLongRunningProcess() {
    await delay(1000);
}
const obs = new PerformanceObserver((items) => {
    console.log('PerformanceObserver A to B',items.getEntries()[0].duration);
    performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });

performance.mark('A');

(async function main(){
    try{
        await performance.timerify(doSomeLongRunningProcess)();
        performance.mark('B');
        performance.measure('A to B', 'A', 'B');
    }catch(e){
        console.log('main() error',e);
    }
})();
Cody G
fuente
1
Me da TypeError: performance.getEntriesByName is not a functionen Nodo v10.4.1
Jeremy Thille
Hice el ejemplo para que puedas ejecutarlo en línea. Es el Nodo 9.7.1. Si no funciona en v10.4.1, entonces me pregunto qué podría estar cambiando.
Cody G
1
Stability: 1 - Experimental¿tal vez? :) nodejs.org/docs/latest-v8.x/api/…
Jeremy Thille
Sí, seguro que ha cambiado. Hay un nuevo observador en v10, puede ver los documentos en nodejs.org/docs/latest-v10.x/api/documentation.html . ¡Actualizaré cuando tenga la oportunidad!
Cody G
19

Para cualquiera que quiera obtener el valor del tiempo transcurrido en lugar de la salida de la consola:

use process.hrtime () como sugerencia de @ D.Deriso, a continuación es mi enfoque más simple:

function functionToBeMeasured() {
    var startTime = process.hrtime();
    // do some task...
    // ......
    var elapsedSeconds = parseHrtimeToSeconds(process.hrtime(startTime));
    console.log('It takes ' + elapsedSeconds + 'seconds');
}

function parseHrtimeToSeconds(hrtime) {
    var seconds = (hrtime[0] + (hrtime[1] / 1e9)).toFixed(3);
    return seconds;
}
Canta
fuente
16
var start = +new Date();
var counter = 0;
for(var i = 1; i < LIMIT; i++){
    ++counter;
    db.users.save({id : i, name : "MongoUser [" + i + "]"}, function(err, saved) {
          if( err || !saved ) console.log("Error");
          else console.log("Saved");
          if (--counter === 0) 
          {
              var end = +new Date();
              console.log("all users saved in " + (end-start) + " milliseconds");
          }
    });
}
Andrey Sidorov
fuente
55
Tuve que buscar la sintaxis '+ new Date ()' para entender qué significaba eso. Según los comentarios sobre esta respuesta ( stackoverflow.com/a/221565/5114 ), no es una buena idea usar ese formulario por razones de rendimiento y legibilidad. Prefiero algo un poco más detallado para que sea más claro para el lector. También vea esta respuesta: stackoverflow.com/a/5036460/5114
Mnebuerquo
3
A menudo uso var start = process.hrtime(); ... var end = process.hrtime(start);para obtener tiempo de alta resolución (si necesito esperar una precisión de menos de milisegundos)
Andrey Sidorov
9

Antigua pregunta, pero para una API simple y una solución ligera; puedes usar perfy que usa process.hrtimeinternamente el tiempo real de alta resolución ( ).

var perfy = require('perfy');

function end(label) {
    return function (err, saved) {
        console.log(err ? 'Error' : 'Saved'); 
        console.log( perfy.end(label).time ); // <——— result: seconds.milliseconds
    };
}

for (var i = 1; i < LIMIT; i++) {
    var label = 'db-save-' + i;
    perfy.start(label); // <——— start and mark time
    db.users.save({ id: i, name: 'MongoUser [' + i + ']' }, end(label));
}

Tenga en cuenta que cada vez que perfy.end(label)se llama, esa instancia se autodestruye.

Divulgación: escribió este módulo, inspirado en la respuesta de D.Deriso . Documentos aquí .

Onur Yıldırım
fuente
2

Podrías probar Benchmark.js . Es compatible con muchas plataformas, entre ellas también node.js.

jsbeckr
fuente
11
Sería bueno si pudiera agregar un ejemplo de cómo usar benchmark.js para este caso de uso.
Petah
2

También puedes probar exectimer . Te da comentarios como:

var t = require("exectimer");

var myFunction() {
   var tick = new t.tick("myFunction");
   tick.start();
   // do some processing and end this tick
   tick.stop();
}

// Display the results
console.log(t.timers.myFunction.duration()); // total duration of all ticks
console.log(t.timers.myFunction.min()); // minimal tick duration
console.log(t.timers.myFunction.max()); // maximal tick duration
console.log(t.timers.myFunction.mean()); // mean tick duration
console.log(t.timers.myFunction.median()); // median tick duration

[editar] Hay incluso una forma más simple de usar exectimer porque ahora puede envolver el código que se va a medir. Su código podría envolverse así:

var t = require('exectimer'),
Tick = t.Tick;

for(var i = 1; i < LIMIT; i++){
    Tick.wrap(function saveUsers(done) {
        db.users.save({id : i, name : "MongoUser [" + i + "]"}, function(err, saved) {
            if( err || !saved ) console.log("Error");
            else console.log("Saved");
            done();
        });
    });
}

// Display the results
console.log(t.timers.myFunction.duration()); // total duration of all ticks
console.log(t.timers.saveUsers.min()); // minimal tick duration
console.log(t.timers.saveUsers.max()); // maximal tick duration
console.log(t.timers.saveUsers.mean()); // mean tick duration
console.log(t.timers.saveUsers.median()); // median tick duration
Alexandru Savin
fuente
0

Y otra opción es usar la herramienta express-debug :

express-debug es una herramienta de desarrollo para express. Es un middleware simple que inyecta resultados de depuración útiles en su html, de una manera no obstructiva.

Ofrece convenientemente un panel de perfiles:

tiempo de procesamiento total requerido. middleware, param y tiempos de ruta.

También. para agregar a las respuestas anteriores, puede verificar esta respuesta para habilitar cualquier código de creación de perfiles solo para el entorno de desarrollo.

Wtower
fuente