Ihr C++-Code läuft langsam? Zeit, unter die Haube zu schauen
\n\nSie haben einen auf den ersten Blick perfekten C++-Code geschrieben. Die Kompilierung war fehlerfrei, das Programm startet... aber es läuft irgendwie träge. Kommt Ihnen bekannt vor? Die Hand zuckt, alles in Assembler umzuschreiben oder schnell Multithreading hinzuzufügen. Keine Eile. Höchstwahrscheinlich liegt das Problem nicht in der Architektur, sondern in ein paar „heißen“ Funktionen, die 90% der CPU-Zeit verschlingen.
\n\nIn der Linux-Welt haben wir ein mächtiges Arsenal an Werkzeugen für das Profiling von C++-Code. Das ist keine Magie, das ist Wissenschaft. Wir raten nicht, wir messen. In diesem Leitfaden zeige ich Ihnen vier praktische Ansätze: vom schnellen „Blick von oben“ bis zum Mikro-Benchmarking. Sie erfahren, wie Sie C++ unter Linux wie ein Profi profilieren.
\n\nWarum Vermutungen böse sind und Profiling gut
\n\nBevor wir in den Code eintauchen, merken Sie sich die goldene Regel der Optimierung: „Optimiere nicht, was nicht gemessen wurde“. Ihre Intuition ist ein schlechter Ratgeber. Oft verbringen wir Stunden mit der Optimierung einer Funktion, die einmal pro Minute aufgerufen wird, und ignorieren eine banale Schleife mit 10 Millionen Iterationen.
\n\nProfiling gibt Antworten auf drei Hauptfragen:
\n- \n
- Wo verbringt das Programm die meiste Zeit? \n
- Warum passiert das (ineffizienter Algorithmus, viele E/A-Operationen, Cache-Fehltritte)? \n
- Was hat sich nach Ihren Änderungen geändert? \n
Heute besprechen wir vier Werkzeuge, die 99% Ihrer Aufgaben abdecken. Los geht's!
\n\nWerkzeug Nr. 1: gprof – der Klassiker
\n\ngprof ist der gute, alte, integrierte GNU-Profiler. Er arbeitet in zwei Schritten: Datensammlung während der Programmausführung und deren Analyse. Vorteile: Sie müssen nichts Zusätzliches installieren, es ist in jeder Distribution enthalten. Nachteile: Es liefert ungefähre Ergebnisse und funktioniert nicht von Haus aus mit Multithread-Programmen.
\n\nSo verwenden Sie gprof
\n\nSchritt 1. Kompilieren Sie das Programm mit dem speziellen Flag -pg:
g++ -pg -O2 my_program.cpp -o my_program\n\nBeachten Sie: Wir verwenden -O2. Einen Debug-Build ohne Optimierungen zu profilieren, ist sinnlos – Sie erhalten ein verzerrtes Bild.
Schritt 2. Starten Sie das Programm. Es wird etwas langsamer laufen, da gprof Daten sammelt.
\n./my_program\n\nSchritt 3. Im aktuellen Ordner erscheint eine Datei gmon.out. Analysieren Sie sie:
gprof my_program gmon.out > analysis.txt\n\nÖffnen Sie analysis.txt und sehen Sie etwas wie:
Flat profile:\nEach sample counts as 0.01 seconds.\n % cumulative self self total\n time seconds seconds calls us/call us/call name\n 60.00 0.06 0.06 10000 6.00 6.00 slow_function\n 20.00 0.08 0.02 5000 4.00 4.00 fast_function\n 10.00 0.09 0.01 main\n\nVoilà! Wir sehen, dass slow_function 60% der Zeit belegt hat. Das ist unser Kandidat für die Optimierung.
Beispielcode für das Profiling
\n\nSchreiben wir ein einfaches Programm, das wir mit allen Werkzeugen profilieren werden:
\n\n#include <iostream>\n#include <cmath>\n\nvoid slow_function(int n) {\n volatile double sum = 0.0; // volatile, damit der Compiler nicht auf Null optimiert\n for (int i = 0; i < n; ++i) {\n for (int j = 0; j < 1000; ++j) {\n sum += std::sin(i) * std::cos(j);\n }\n }\n std::cout << sum << std::endl;\n}\n\nvoid fast_function(int n) {\n volatile int count = 0;\n for (int i = 0; i < n; ++i) {\n count += i % 2;\n }\n std::cout << count << std::endl;\n}\n\nint main() {\n slow_function(10000);\n fast_function(50000);\n return 0;\n}\n\nKompilieren Sie mit dem Flag -pg und starten Sie