Wie messe ich die Ausführungszeit von JavaScript-Code mit Rückrufen?


319

Ich habe einen Teil des JavaScript-Codes, den ich mit dem node.jsInterpreter ausführe .

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

Wie kann ich die Zeit messen, die diese Datenbankeinfügevorgänge benötigen? Ich könnte die Differenz der Datumswerte nach und vor diesem Code berechnen, aber das wäre aufgrund der asynchronen Natur des Codes falsch.


7
Lesen Sie einfach die Startzeit vor dem
Datenbankaufruf

Es besteht die Möglichkeit, dass die Zeit, zu der der DB das Einfügen beendet und der Rückruf ausgeführt wird, nicht gleich ist und dies zu einem Fehler bei der Messung führen würde.
Sturmschatten

1
Nein, Sie sollten sich keine Sorgen machen. Wenn der Code der Datenbankbibliothek gut gestaltet ist und vor dem Auslösen des Rückrufs keine andere Operation ausführt, sollten Sie eine gute Maßnahme ergreifen. Sie können die Einfügung auch profilieren, indem Sie die Zeitstempel in den Bibliothekscode einfügen, in dem die Einfügung tatsächlich ausgeführt wird, anstatt in Ihren eigenen, aber ich würde mir auch darüber keine Sorgen machen
BFil

Ich würde empfehlen, NodeTime auszuprobieren, was gut zu dem passt, was Sie versuchen.
Julian Knight

Ich habe geschrieben, timerlogwas ähnlich ist, console.time()aber mit zusätzlichen Funktionen; github.com/brillout/timerlog
brillout

Antworten:


718

Verwenden Sie die Node.js console.time()und 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
Saubere und integrierte Lösung für Knoten.
Behlül Uçar

45
> Ich möchte wissen, wie die Zeit gemessen wird, die diese DB-Einfügeoperationen benötigen. --- console.timeEnd ("dbsave") gibt nur aus, um das Timing zu trösten. Sie können das nicht weiter nutzen und sind weniger flexibel. Wenn Sie den tatsächlichen Timing-Wert benötigen, wie in der ursprünglichen Frage, können Sie console.timeEnd ("dbsave")
gogaman

@gogaman Dies ist ein guter Punkt, da Sie die Ausgabe von console.timeEnd () nicht erfassen können. Vielleicht kann es nützlich sein, die Ausgabe in eine Datei zu leiten und von dort aus zu verwenden?
Doug Molineux

5
Was ist der Unterschied zwischen console.time () und process.hrtime () in der folgenden Antwort?
Gelb-Heiliger

3
Es lohnt sich, einen Hinweis hinzuzufügen, dass die Ausführungszeit dann ausgedruckt wird, nur damit jetzt neue Benutzer.
Janko-m

208

Dafür gibt es eine Methode. Check out process.hrtime (); .

Also habe ich dies im Grunde ganz oben in meiner App platziert.

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
}

Dann benutze ich es, um zu sehen, wie lange Funktionen dauern. Hier ist ein einfaches Beispiel, das den Inhalt einer Textdatei mit dem Namen "output.txt" druckt:

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

Hier ist ein kurzer Test, den Sie in einem Terminal (BASH-Shell) ausführen können:

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

3
Ist das der console.time-Lösung in irgendeiner Weise überlegen?
Scravy

31
Ja, es ist viel genauer und Sie können das Ergebnis in einer Variablen speichern
Dallas Clark

Dieser funktioniert für mich, da ich den Timer mehrmals
anrufen

2
Warum rufst du process.hrtime(start)zweimal an? Gibt es einen bestimmten Grund dafür?
Sohail Si

1
process.hrtime ([time]), wobei time ein optionaler Parameter ist, der das Ergebnis eines vorherigen Aufrufs von process.hrtime () sein muss, um sich von der aktuellen Zeit zu unterscheiden. Es gibt den Unterschied zwischen dem aktuellen Anruf und dem vorherigen Stundenaufruf an.
Nilesh Jain

72

Beim Aufrufen console.time('label')wird die aktuelle Zeit in Millisekunden aufgezeichnet. Bei einem späteren Aufruf console.timeEnd('label')wird die Dauer ab diesem Zeitpunkt angezeigt.

Die Zeit in Millisekunden wird automatisch neben dem Etikett gedruckt, sodass Sie console.log nicht separat aufrufen müssen, um ein Etikett zu drucken:

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

Weitere Informationen finden Sie in den Entwicklerdokumenten von Mozillaconsole.time .


Was trägt dies zur akzeptierten Antwort bei ?
Dan Dascalescu

1
Die akzeptierte Antwort wurde nach meiner Antwort geändert, um meinen Code zu verwenden
jfcorugedo

23

Überrascht hatte noch niemand die neu eingebauten Bibliotheken erwähnt:

Verfügbar in Node> = 8.5 und sollte in Modern Browers sein

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

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

Knoten 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

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

Gibt mir TypeError: performance.getEntriesByName is not a functionin Node v10.4.1
Jeremy Thille

Ich habe das Beispiel gemacht, damit Sie es online ausführen können. Es ist Knoten 9.7.1. Wenn es in Version 10.4.1 nicht funktioniert, frage ich mich, was sich möglicherweise ändert!
Cody

1
Stability: 1 - Experimentalkönnte sein? :) nodejs.org/docs/latest-v8.x/api/…
Jeremy Thille

Ja sicher hat es sich geändert. In Version 10 gibt es einen neuen Beobachter. Die Dokumente finden Sie unter nodejs.org/docs/latest-v10.x/api/documentation.html . Ich werde aktualisieren, wenn ich die Chance bekomme!
Cody G

19

Für alle, die anstelle der Konsolenausgabe einen Wert für die verstrichene Zeit erhalten möchten:

Verwenden Sie process.hrtime () als @ D.Deriso-Vorschlag. Unten ist mein einfacherer Ansatz:

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
Ich musste die Syntax '+ new Date ()' nachschlagen, um herauszufinden, was das bedeutete. Laut Kommentaren zu dieser Antwort ( stackoverflow.com/a/221565/5114 ) ist es aus Leistungsgründen und aus Gründen der Lesbarkeit keine gute Idee, dieses Formular zu verwenden. Ich bevorzuge etwas ausführlicheres, damit es für den Leser klarer ist. Siehe auch diese Antwort: stackoverflow.com/a/5036460/5114
Mnebuerquo

3
Ich benutze oft var start = process.hrtime(); ... var end = process.hrtime(start);, um eine hohe Auflösungszeit zu erhalten (wenn ich eine Genauigkeit von weniger als einer Millisekunde erwarten muss)
Andrey Sidorov

9

Alte Frage, aber für eine einfache API und eine leichte Lösung; Sie können perfy verwenden, das process.hrtimeintern hochauflösende Echtzeit ( ) verwendet.

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

Beachten Sie perfy.end(label), dass diese Instanz bei jedem Aufruf automatisch zerstört wird.

Offenlegung: Schrieb dieses Modul, inspiriert von D.Derisos Antwort . Docs hier .


2

Sie könnten Benchmark.js ausprobieren. Es unterstützt viele Plattformen, darunter auch node.js.


11
Wäre gut, wenn Sie ein Beispiel für die Verwendung von Benchmark.js für diesen Anwendungsfall hinzufügen könnten.
Petah

2

Sie können auch Exectimer ausprobieren . Es gibt Ihnen Feedback wie:

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

[Bearbeiten] Es gibt jetzt noch eine einfachere Möglichkeit, Exectimer zu verwenden, da jetzt der zu messende Code umbrochen werden kann. Ihr Code könnte folgendermaßen verpackt werden:

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

Eine weitere Option ist die Verwendung des Express-Debug- Tools:

Express-Debug ist ein Entwicklungswerkzeug für Express. Es ist eine einfache Middleware, die nützliche HTML-Ausgabe auf nicht behindernde Weise in Ihr HTML einfügt.

Es bietet bequem ein Profiling-Panel:

Gesamtanforderungsverarbeitungszeit. Middleware-, Parameter- und Routen-Timings.

Ebenfalls. Um die obigen Antworten zu ergänzen, können Sie diese Antwort überprüfen , um Profiling-Code nur für die Entwicklungsumgebung zu aktivieren.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.