Profilierung von JavaScript-Code: Engpässe für Anfänger finden

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Profilierung von JavaScript-Code: Engpässe finden

\n\n

Wenn Ihr Node.js-Projekt anfängt zu stocken, ist der erste Gedanke: „Ich muss alles in Rust neu schreiben.“ Aber beeilen Sie sich nicht! Meistens liegt das Problem in ein paar Zeilen Code, die ineffizient arbeiten. In diesem Artikel schauen wir uns an, wie Sie diese „Flaschenhälse“ mit einfachen Profilierungswerkzeugen finden. Sie erfahren, wie Sie die Leistung messen, ein Flamegraph erstellen und verstehen, wohin die CPU-Zeit fließt.

\n\n

Warum Code profilieren?

\n\n

Stellen Sie sich vor, Ihr Server verarbeitet 100 Anfragen pro Sekunde, benötigt werden aber 500. Sie könnten alles Mögliche optimieren, aber das würde Wochen dauern. Oder Sie finden eine Funktion, die 80% der Zeit beansprucht, und beheben sie in einer Stunde. Profilierung zeigt genau, wo die Zeit verbraucht wird.

\n\n

Betrachten wir die wichtigsten Szenarien, in denen eine Profilierung notwendig ist:

\n
    \n
  • Hohe Antwortverzögerung bei APIs
  • \n
  • Speicherlecks (memory leaks)
  • \n
  • Hohe CPU-Auslastung ohne ersichtlichen Grund
  • \n
  • Langsame Verarbeitung großer Datenarrays
  • \n
\n\n

Werkzeug 1: Integrierter Profiler von Node.js (--prof)

\n\n

Der einfachste Weg ist, Node.js mit dem Flag --prof zu starten. Es sammelt Ausführungsstatistiken und speichert sie in einer Datei isolate-*.log.

\n\n

Erstellen wir ein Testskript, das einen Engpass enthält:

\n\n
// slow.js\nfunction heavyComputation() {\n    let sum = 0;\n    for (let i = 0; i < 1e7; i++) {\n        sum += Math.sqrt(i) * Math.sin(i);\n    }\n    return sum;\n}\n\nfunction fastFunction() {\n    return 42;\n}\n\nconsole.time('total');\nheavyComputation();\nfastFunction();\nconsole.timeEnd('total');\n
\n\n

Starten wir die Profilierung:

\n
node --prof slow.js
\n\n

Nach der Ausführung erscheint eine Datei isolate-0x...-v8.log. Um sie zu lesen, verwenden wir das Dienstprogramm node --prof-process:

\n
node --prof-process isolate-0x...-v8.log > profile.txt
\n\n

Öffnen wir profile.txt und sehen eine Tabelle mit Zeitprozenten:

\n
[Summary]:\n   ticks  total  nonlib   name\n   785    78.5%  85.3%   JavaScript\n   215    21.5%  14.7%   C++\n   ...\n[Bottom up (heavy) profile]:\n  ticks parent  name\n   500   50.0%   LazyCompile: *heavyComputation slow.js:2:17\n   100   10.0%   Builtin: MathSqrt\n    50    5.0%   Builtin: MathSin\n
\n\n

Wir sehen, dass 50% der Zeit für die Funktion heavyComputation aufgewendet werden. Jetzt wissen wir, was zuerst optimiert werden muss.

\n\n

Werkzeug 2: Chrome DevTools (Inspektor)

\n\n

Für eine anschaulichere Analyse verbinden wir Chrome DevTools. Starten Sie Node.js mit dem Flag --inspect:

\n
node --inspect slow.js
\n\n

Öffnen Sie Chrome und gehen Sie zu chrome://inspect. Klicken Sie auf „Open dedicated DevTools for Node“. Reiter „Profiler“ → „Start“ → führen Sie das Skript aus → „Stop“.

\n\n

Sie sehen ein Flamegraph – ein Diagramm, bei dem die Breite eines Blocks die Ausführungszeit anzeigt. Je breiter der Block, desto „heißer“ die Funktion. Unser heavyComputation wird ein riesiges Rechteck sein.

\n\n

Tipp: Verwenden Sie den Filter „Focus selected“ auf einer Funktion, um ihre internen Aufrufe zu sehen. Sie werden zum Beispiel feststellen, dass Math.sqrt 10 Millionen Mal aufgerufen wird – vielleicht lohnt es sich, die Wurzeln vorab zu berechnen.

\n\n

Werkzeug 3: perf_hooks – präzise Messungen im Code

\n\n

Manchmal muss man einen bestimmten Codeabschnitt messen, ohne externe Werkzeuge zu starten. Dafür verwenden wir das integrierte Modul perf_hooks.

\n\n

Beispiel für die Messung der Ausführungszeit einer Funktion:

\n\n
const { performance } = require('perf_hooks');\n\nfunction processData(items) {\n    let result = [];\n    for (let item of items) {\n        // Simulation einer komplexen Verarbeitung\n        for (let j = 0; j < 1000; j++) {\n            result.push(item * j);\n        }\n                

Blogs

Buchempfehlungen