Comment mesurer le temps d'exécution du code JavaScript avec des rappels?


320

J'ai un morceau de code JavaScript que j'exécute à l'aide de l' node.jsinterpréteur.

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

Comment puis-je mesurer le temps pris par ces opérations d'insertion de base de données? Je pourrais calculer la différence des valeurs de date après et avant ce morceau de code, mais ce serait incorrect en raison de la nature asynchrone du code.


8
Il suffit de lire l'heure de début avant l'appel db, et l'heure de fin à l'intérieur du rappel ..
BFil

Il est possible que l'heure à laquelle la base de données termine l'insertion et l'heure à laquelle le rappel est exécuté ne soient pas les mêmes et cela introduirait une erreur dans la mesure?
Stormshadow

1
Non, vous ne devriez pas vous en inquiéter, si le code de la bibliothèque db est bien conçu et ne gère aucune autre opération avant de lancer le rappel, vous devriez obtenir une bonne mesure. Vous pouvez également profiler l'insertion en plaçant les horodatages dans le code de la bibliothèque où l'insertion est réellement effectuée, au lieu du vôtre, mais, encore une fois, je ne m'en inquiéterais pas
BFil

Je recommanderais d'essayer NodeTime qui semble être un bon choix pour ce que vous essayez de faire.
Julian Knight

J'ai écrit timerlogce qui est similaire console.time()mais avec des fonctionnalités supplémentaires; github.com/brillout/timerlog
brillout

Réponses:


720

Utilisez Node.js console.time()et 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
Solution propre et intégrée pour le nœud.
Behlül Uçar

45
> Je veux savoir comment mesurer le temps pris par ces opérations d'insertion db. --- console.timeEnd ("dbsave") sort juste pour consolider le timing. Vous ne pouvez pas l'utiliser davantage et est moins flexible. Si vous avez besoin de la valeur de synchronisation réelle, comme dans la question d'origine, vous ne pouvez pas utiliser console.timeEnd ("dbsave")
gogaman

@gogaman c'est un bon point, car vous ne pouvez pas capturer la sortie de console.timeEnd (). Peut-être qu'il pourrait être utile de diriger la sortie vers un fichier et de l'utiliser à partir de là?
Doug Molineux

5
Alors, quelle est la différence entre console.time () et process.hrtime () dans la réponse ci-dessous?
yellow-saint

3
Il serait utile d'ajouter une note indiquant que le temps d'exécution est ensuite imprimé, juste pour que les nouveaux utilisateurs puissent maintenant.
janko-m

208

Il existe une méthode conçue pour cela. Découvrez process.hrtime (); .

Donc, j'ai essentiellement mis cela en haut de mon application.

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
}

Ensuite, je l'utilise pour voir combien de temps les fonctions prennent. Voici un exemple de base qui imprime le contenu d'un fichier texte appelé "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);

Voici un test rapide que vous pouvez exécuter dans un terminal (shell BASH):

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

3
est-ce supérieur à la solution console.time de quelque façon que ce soit?
scravy

31
Oui, c'est beaucoup plus précis et vous pouvez stocker le résultat dans une variable
Dallas Clark

Celui-ci fonctionne pour moi, car je voulais appeler la minuterie plusieurs fois
tbh__

2
Pourquoi appelez-vous process.hrtime(start)deux fois? Y a-t-il une raison particulière à cela?
Sohail Si

1
process.hrtime ([time]), où time est un paramètre facultatif qui doit être le résultat d'un appel process.hrtime () précédent à diff avec l'heure actuelle. Il donne la différence entre l'appel en cours et l'appel hrtime précédent.
Nilesh Jain

72

L' console.time('label')appel enregistrera l'heure actuelle en millisecondes, puis l'appel ultérieur console.timeEnd('label')affichera la durée à partir de ce point.

Le temps en millisecondes sera automatiquement imprimé à côté de l'étiquette, vous n'avez donc pas à appeler séparément console.log pour imprimer une étiquette:

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

Pour plus d'informations, consultez la documentation des développeurs de Mozilla surconsole.time .


Qu'est-ce que cela ajoute à la réponse acceptée ?
Dan Dascalescu

1
La réponse acceptée a été modifiée après ma réponse pour utiliser mon code
jfcorugedo

24

Surpris, personne n'avait encore mentionné la nouvelle bibliothèque intégrée:

Disponible dans Node> = 8.5, et devrait être dans Modern Browers

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

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

Noeud 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

Noeud 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
Me donne TypeError: performance.getEntriesByName is not a functiondans Node v10.4.1
Jeremy Thille

J'ai fait l'exemple pour que vous puissiez l'exécuter en ligne. Il s'agit du nœud 9.7.1. Si cela ne fonctionne pas dans la v10.4.1, je me demande ce qui pourrait changer!
Cody

1
Stability: 1 - Experimentalpeut être? :) nodejs.org/docs/latest-v8.x/api/…
Jeremy Thille

Oui, c'est sûr que ça a changé. Il y a un nouvel observateur dans la v10, vous pouvez voir les documents sur nodejs.org/docs/latest-v10.x/api/documentation.html . Je mettrai à jour quand j'en aurai l'occasion!
Cody

19

Pour quiconque souhaite obtenir la valeur du temps écoulé au lieu de la sortie de la console:

utilisez process.hrtime () comme suggestion @ D.Deriso, voici mon approche plus 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;
}

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
J'ai dû chercher la syntaxe '+ new Date ()' pour comprendre ce que cela signifiait. Selon les commentaires sur cette réponse ( stackoverflow.com/a/221565/5114 ), ce n'est pas une bonne idée d'utiliser ce formulaire pour des raisons de performances ainsi que de lisibilité. Je préfère quelque chose d'un peu plus verbeux donc c'est plus clair pour le lecteur. Voir également cette réponse: stackoverflow.com/a/5036460/5114
Mnebuerquo

3
J'utilise souvent var start = process.hrtime(); ... var end = process.hrtime(start);pour obtenir un temps de haute résolution (si je dois m'attendre à une précision inférieure à la milliseconde)
Andrey Sidorov

9

Vieille question mais pour une API simple et une solution légère; vous pouvez utiliser perfy qui utilise en temps réel haute résolution ( process.hrtime) en interne.

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

Notez qu'à chaque perfy.end(label)appel, cette instance est automatiquement détruite.

Divulgation: A écrit ce module, inspiré par la réponse de D.Deriso . Documents ici .


2

Vous pouvez essayer Benchmark.js . Il prend en charge de nombreuses plates-formes, dont node.js.


11
Ce serait bien si vous pouviez ajouter un exemple d'utilisation de benchmark.js pour ce cas d'utilisation.
Petah

2

Vous pouvez également essayer exectimer . Il vous donne des commentaires comme:

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

[edit] Il y a même un moyen plus simple maintenant d'utiliser exectimer car maintenant il peut envelopper le code à mesurer. Votre code pourrait être enveloppé comme ceci:

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

Et une autre option consiste à utiliser l' outil de débogage express :

express-debug est un outil de développement pour express. C'est un middleware simple qui injecte une sortie de débogage utile dans votre html, de manière non obstructive.

Il offre commodément un panneau de profilage:

temps total de traitement des demandes. middleware, param et timings de route.

Aussi. pour ajouter aux réponses ci-dessus, vous pouvez cocher cette réponse pour activer tout code de profilage uniquement pour l'environnement de développement.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.