Das Problem der Messung der Laufzeit Ihrer Node.js-App
Das Messen der Laufzeit einer Node.js-Anwendung ist ein entscheidender Aspekt der Leistungsoptimierung. Indem Sie messen, wie lange die Ausführung verschiedener Teile Ihrer Anwendung dauert, können Sie Engpässe identifizieren und Ihren Code optimieren, um die Leistung zu verbessern.
Wie können wir die Zeitausführung einer Funktion messen?
Es gibt viele Möglichkeiten, die Zeit in node.js zu messen. Die hochauflösendeste Methode hierfür ist die Verwendung von „ process.hrtime()“ oder „ Perf_hooks “. Beide sind besser als die Verwendung der Date-Klasse, da sie sich nicht auf die Systemuhr stützen. Dennoch gibt es nur wenige Unterschiede zwischen ihnen:
Process.hrtime() bietet eine Auflösung im Nanosekundenbereich, während perf_hooks eine Auflösung im Mikrosekundenbereich bietet. Dies bedeutet, dass „process.hrtime()“ im Allgemeinen präziser, aber auch teurer im Aufruf ist.
Sehen wir uns nun ein Beispiel für die Messung der Laufzeit der folgenden Logik an. Wir haben eine asynchrone Logik (wir können davon ausgehen, dass es sich um einen HTTP-Aufruf handelt), die nach 1000 ms aufgelöst wird. Wir werden perf_hooks verwenden, um zu messen, wie lange es dauert, dieses Versprechen zu erfüllen.
const {performance} = require('perf_hooks');
const SOME_BIG_NUMBER = 4000000000;
async function asyncLogic(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const start = performance.now();
asyncLogic(1000).then(() => {
const time = performance.now() - start;
console.log(`Time taken: ${time}ms`);
});
let x = 0;
for (let i = 0; i < SOME_BIG_NUMBER; i++) {
x += Math.sqrt(SOME_BIG_NUMBER);
}
Time taken: 3948ms
Die Ereignisschleife und die Ereigniswarteschlange
Das JavaScript-Laufzeitmodell basiert auf der Ereignisschleife. Dies ist der Teil, der für die Ausführung unseres Codes verantwortlich ist und uns die Verwendung asynchroner Logik ermöglicht.
Wenn wir JavaScript-Code ausführen, passiert Folgendes:
- JavaScript führt den gesamten synchronen Code in der Reihenfolge aus, in der er im Programm erscheint.
- Wenn asynchroner Code vorhanden ist (z. B. unsere asyncLogic-Funktion), fügt JavaScript ihn der Ereigniswarteschlange hinzu , anstatt ihn sofort auszuführen.
- Die Ereignisschleife überprüft ständig die Ereigniswarteschlange , um festzustellen, ob Ereignisse auf die Ausführung warten.
- Wenn ein Ereignis zur Ausführung bereit ist, wird es von der Ereignisschleife aus der Ereigniswarteschlange in den Aufrufstapel verschoben , wo es ausgeführt werden kann.
- Sobald das Ereignis ausgeführt wird, wird jeglicher zusätzlicher synchroner Code ausgeführt, bevor die Ereignisschleife zurückgeht, um die Ereigniswarteschlange auf weitere Ereignisse zu überprüfen.
Deshalb betrug in unserem Beispiel die Zeitmessung 4000ms,
- Wir führen die asyncLogic-Funktion aus, die zur Ereigniswarteschlange hinzugefügt wurde.
- Dann führen wir unsere synchrone Logik aus, deren Ausführung etwa 4000 ms dauert.
- Die Ereignisschleife verschiebt unseren asyncLogic()-Aufruf zurück aus der Ereigniswarteschlange in den Aufrufstapel
- Anschließend führen wir die Callback-Funktion aus und berechnen die Endzeit
Verzögerung der Ereignisschleife
Daher gibt es bei der Überwachung unserer Anwendungsleistung eine wichtige Metrik, auf die wir achten sollten: die Verzögerung der Ereignisschleife, die Zeit, die die Ereignisschleife vom Hinzufügen eines neuen Rückrufs zur Ereigniswarteschlange bis zur Ausführung dieses Rückrufs benötigt.
Mit dem folgenden Code können wir ganz einfach eine Schleife erstellen, mit der wir die Verzögerung unserer Ereignisschleife messen können:
function measureEventLoopLag() {
let lastLoopTime = performance.now();
setTimeout(() => {
const delay = performance.now() - lastLoopTime - 1000;
lastLoopTime = performance.now();
console.log(`Event Loop lag is: ${delay} ms`)
measureEventLoopLag();
}, 1000)
}
Laufzeitverbesserung
Nehmen wir an, dass unser Code einen HTTP-Aufruf hat, der 4000 ms dauert, und wir haben auch eine langsame Schleife, deren Ausführung ebenfalls etwa 4000 ms dauert. Wie wir in unserem vorherigen Beispiel gesehen haben, werden wir bei der Messung dieser Laufzeit feststellen, dass wir 4000 ms brauchten, um den gesamten Code auszuführen.
Nach der Umgestaltung unseres Codes reduzieren wir die Laufzeit der langsamen Schleife um 50 %. Als wir den Code erneut ausführten, änderte sich die Gesamtlaufzeit unserer Messung zufolge nicht wirklich. Wir warteten immer noch etwa 4000 ms, um den Gesamtfluss abzuschließen, da wir darauf warteten, dass unser asynchroner Fluss abgeschlossen war. Scheint, als hätte unser Refactor nicht wirklich geholfen.
Nicht wirklich, wenn wir einen Blick auf die CPU werfen, haben wir einen großen Unterschied gemacht. Unsere HTTP-Aufrufe verbrauchen nicht viel CPU, wir warten meist nur auf die Antwort. Die meiste Zeit hat also unsere CPU unsere langsame Schleife ausgeführt. Als wir den Code umgestalteten und ihn um 50 % reduzierten, änderte sich die Gesamtzeit nicht, aber es hatte einen erheblichen Einfluss auf unsere CPU.
Abschluss
In unserem Beispiel mussten wir eine sehr kleine Logik überwachen. Die Sache wird wirklich kompliziert, wenn Sie ein komplexes System mit viel auszuführender Logik haben. In diesem Fall sollten Sie versuchen, kleine Teile Ihrer Logik zu messen, um den Leistungsabfall besser überwachen zu können.
Bei der Überwachung der Laufzeit Ihrer Anwendung sind zwei wichtige Dinge zu beachten:
- Die Messung der asynchronen Funktionszeit kann durch synchronen Code beeinflusst werden (Verzögerung der Ereignisschleife).
- Messfunktionen, die sowohl über asynchrone als auch synchrone Logik verfügen, können die Verbesserung (oder Verschlechterung) der Laufzeit verbergen.
- Eine Verzögerung der Ereignisschleife von mehr als ~30 ms ist für Ihre Anwendung wirklich schlecht.

![Was ist überhaupt eine verknüpfte Liste? [Teil 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































