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.