Come misuro il tempo di esecuzione del codice JavaScript con i callback?


319

Ho un pezzo di codice JavaScript che sto eseguendo usando l' node.jsinterprete.

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");
    }
  });
}

Come posso misurare il tempo impiegato da queste operazioni di inserimento del database? Potrei calcolare la differenza dei valori di data dopo e prima di questo pezzo di codice, ma sarebbe errato a causa della natura asincrona del codice.


8
Basta leggere l'ora di inizio prima della chiamata db e l'ora di fine DENTRO la richiamata.
— BFil

Esiste la possibilità che il tempo in cui il DB termina l'inserimento e il tempo in cui viene eseguita la richiamata non coincidano e ciò comporterebbe un errore nella misurazione?
— Stormshadow

1
No, non dovresti preoccuparti, se il codice della libreria db è ben progettato e non gestisce altre operazioni prima di attivare il callback, dovresti ottenere una buona misura. Puoi anche profilare l'inserimento inserendo i timestamp all'interno del codice della libreria in cui viene effettivamente eseguito l'inserimento, anziché il tuo, ma, di nuovo, non mi preoccuperei
— BFil

Consiglierei di provare NodeTime che sembra essere adatto a ciò che stai cercando di fare.
— Julian Knight,

Ho scritto timerlogche è simile console.time()ma con funzionalità aggiuntive; github.com/brillout/timerlog
— brillout

Risposte:


719

Usa Node.js console.time()e 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");}
};

31
Soluzione pulita e integrata per il nodo.
— Behlül Uçar,

45
> Voglio sapere come misurare il tempo impiegato da queste operazioni di inserimento db. --- console.timeEnd ("dbsave") genera solo output per consolare i tempi. Non puoi usarlo ulteriormente ed è meno flessibile. Se hai bisogno del valore di temporizzazione effettivo, come nella domanda originale, non puoi usare console.timeEnd ("dbsave")
— gogaman

@gogaman questo è un buon punto, dal momento che non puoi catturare l'output da console.timeEnd (). Forse potrebbe essere utile reindirizzare l'output in un file e utilizzarlo da lì?
— Doug Molineux,

5
Quindi qual è la differenza tra console.time () e process.hrtime () nella risposta di seguito?
— giallo-santo,

3
Varrebbe la pena aggiungere una nota che il tempo di esecuzione viene quindi stampato, così che ora i nuovi utenti.
— janko-m,

208

Esiste un metodo progettato per questo. Dai un'occhiata a process.hrtime (); .

Quindi, l'ho praticamente inserito nella parte superiore della mia app.

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
}

Quindi lo uso per vedere quanto tempo impiegano le funzioni. Ecco un esempio di base che stampa il contenuto di un file di testo chiamato "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);

Ecco un breve test che puoi eseguire in un terminale (shell BASH):

for i in {1..100}; do echo $i; curl http://localhost:8080/; done

3
che superiore alla soluzione console.time in qualche modo?
— scravy

31
Sì, è molto più preciso e puoi archiviare il risultato in una variabile
— Dallas Clark

Questo funziona per me, dal momento che volevo chiamare più volte il timer
— tbh__

2
Perché chiami process.hrtime(start)due volte? C'è un motivo particolare per questo?
— Sohail Si,

1
process.hrtime ([time]), dove time è un parametro facoltativo che deve essere il risultato di una precedente chiamata process.hrtime () per diff con l'ora corrente. Dà la differenza tra la chiamata corrente e la precedente chiamata dell'ora.
— Nilesh Jain,

72

L'invocazione console.time('label')registrerà l'ora corrente in millisecondi, quindi la chiamata successiva console.timeEnd('label')visualizzerà la durata da quel punto.

Il tempo in millisecondi verrà automaticamente stampato accanto all'etichetta, quindi non è necessario effettuare una chiamata separata a console.log per stampare un'etichetta:

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

Per ulteriori informazioni, consultare i documenti per sviluppatori di Mozilla suconsole.time .


Cosa aggiunge questo alla risposta accettata ?
— Dan Dascalescu,

1
La risposta accettata è stata modificata dopo la mia risposta per utilizzare il mio codice
— jfcorugedo,

24

Sorpreso nessuno aveva menzionato ancora le nuove librerie integrate:

Disponibile nel nodo> = 8.5 e dovrebbe essere nei browser moderni

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);
    }
})();

1
Mi dà TypeError: performance.getEntriesByName is not a functionin Nodo v10.4.1
— Jeremy Thille il

Ho fatto l'esempio in modo che tu possa eseguirlo online. È il nodo 9.7.1. Se non funziona in v10.4.1, mi chiedo cosa potrebbe cambiare!
— Cody G il

1
Stability: 1 - Experimentalpuò essere? :) nodejs.org/docs/latest-v8.x/api/…
— Jeremy Thille

Sì, sicuramente è cambiato. C'è un nuovo osservatore in v10, puoi vedere i documenti su nodejs.org/docs/latest-v10.x/api/documentation.html . Aggiornerò quando ne avrò la possibilità!
— Cody G

19

Per chiunque desideri ottenere il valore del tempo trascorso anziché l'output della console:

uso process.hrtime () come suggerimento @ D.Deriso, di seguito è il mio approccio più semplice:

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;
}

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");
          }
    });
}

5
Ho dovuto cercare la sintassi '+ new Date ()' per capire cosa significasse. Secondo i commenti su questa risposta ( stackoverflow.com/a/221565/5114 ), non è una buona idea utilizzare quel modulo per motivi di prestazioni e leggibilità. Preferisco qualcosa di un po 'più dettagliato, quindi è più chiaro per il lettore. Si veda anche questa risposta: stackoverflow.com/a/5036460/5114
— Mnebuerquo

3
Uso spesso var start = process.hrtime(); ... var end = process.hrtime(start);per ottenere tempi di risoluzione elevati (se devo aspettarmi una precisione inferiore al millisecondo)
— Andrey Sidorov,

9

Vecchia domanda ma per una semplice API e una soluzione leggera; puoi usare perfy che usa process.hrtimeinternamente il tempo reale ad alta risoluzione ( ).

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));
}

Si noti che ogni volta che perfy.end(label)viene chiamato, quell'istanza viene automaticamente distrutta.

Divulgazione: ha scritto questo modulo, ispirato alla risposta di D.Deriso . Documenti qui .


2

Puoi provare Benchmark.js . Supporta molte piattaforme tra cui anche node.js.


11
Sarebbe utile se si potesse aggiungere un esempio di come utilizzare benchmark.js per questo caso d'uso.
— Petah,

2

Potresti anche provare il Exectimer . Ti dà feedback come:

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

[modifica] Ora c'è anche un modo più semplice di usare l'esectimer perché ora può racchiudere il codice da misurare. Il tuo codice potrebbe essere racchiuso in questo modo:

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


0

E un'altra opzione è usare lo strumento express-debug :

express-debug è uno strumento di sviluppo per express. È un semplice middleware che inserisce utili output di debug nel tuo HTML, in modo non ostruttivo.

Offre comodamente un pannello di profilazione:

tempo totale di elaborazione del req. tempi di middleware, param e route.

Anche. per aggiungere le risposte di cui sopra, è possibile controllare questa risposta per abilitare qualsiasi codice di profilazione solo per l'ambiente di sviluppo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.