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.

Stormshadow
la source
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");}
};
user2362662
la source
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
D.Deriso
la source
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 .

jfcorugedo
la source
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);
    }
})();
Cody G
la source
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;
}
Chanter
la source
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
la source
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 .

Onur Yıldırım
la source
2

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

jsbeckr
la source
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
Alexandru Savin
la source
1

J'ai eu le même problème lors du passage d'AWS à Azure

Pour express & aws, vous pouvez déjà utiliser, time () et timeEnd () existants

Pour Azure, utilisez ceci: https://github.com/manoharreddyporeddy/my-nodejs-notes/blob/master/performance_timers_helper_nodejs_azure_aws.js

Ces fonctions time () et timeEnd () utilisent la fonction hrtime () existante, qui donne du temps réel à haute résolution.

J'espère que cela t'aides.

Manohar Reddy Poreddy
la source
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.

Wtower
la source