Programm DEUTLICH langsamer bei Verwendung über TTY

Programm DEUTLICH langsamer bei Verwendung über TTY

Ich habe also ein Programm in C++ geschrieben.

Es kann mir sagen, wie lange alle Berechnungen gedauert haben, und es führt viele recht umfangreiche Berechnungen mit mehreren Threads durch.

Mir ist gerade aufgefallen, dass es beim Ausführen des Programms auf genau derselben Maschine etwa 20 bis 21 Sekunden dauert, um alle Berechnungen durchzuführen, wenn ich es vom TTY aus starte, und nur etwa 0,2 Sekunden, wenn ich es vom GNOME-Terminal aus starte.

Was ist die Ursache dafür? Es ist buchstäblich genau dieselbe Datei auf demselben Computer.

Antwort1

Einige theoretische Hintergründe

CTRLNun, sowohl das, womit Sie nach + ALT+ arbeiten F1, als auch das GNOME-Terminal sind unterschiedliche Implementierungen desselben Konzepts:emulierenein sogenanntes Vollbild-Terminal.

Ersteres wird in Linux als virtuelles Terminal (VT) oder normalerweise einfach als „Konsole“ bezeichnet. Es verwendet einen speziellen „Nur-Text“-Videomodus, der noch immer von den Hardware-Grafikkarten auf den x86-kompatiblen Plattformen (also denen aus der „IBM PC“-Familie) bereitgestellt wird. Letzteres ist eine GUI-Anwendung.

Beide bieten Anwendungen, die mit ihrer Hilfe ausgeführt werden, eine Reihe von Funktionen, die solche Anwendungen von einem „Endgerät“ erwarten (weitere Details und Hinweise—Hier).

Das vorliegende Problem

OK, kommen wir nun zur wahrgenommenen Langsamkeit.

Ich bin sicher, dass der Kern Ihres Problems darin liegt, dass Ihr Programm sogenannte „blockierende“ I/Os durchführt. Das heißt, jedes Mal, wenn Sie etwas tun wie

std::cout << "Hello, world" << endl;

in Ihrem Code, greift zunächst der Code der mit Ihrer Anwendung verknüpften C++-Standardbibliothek und kümmert sich um die Ausgabe der an die angegebeneStrom.

Nach einer gewissen Verarbeitung (und meist einer Pufferung) müssen diese Daten den laufenden Prozess Ihres Programms tatsächlich verlassen und tatsächlich auf das Medium ausgegeben werden, an das Ihr Programm seine Ausgabe sendet. Unter Linux (und anderen Unix-kompatiblen Systemen) erfordert dies einen Aufruf des Kernels – über einen dediziertenSystemaufruf(oderSystemaufrufkurz) mit dem Namen write().

Die C++-Standardbibliothek führt diesen write()Systemaufruf letztendlich aus und wartet dann auf dessen Abschluss, d. h., sie wartet auf die Antwort des Kernels: „OK, der Empfänger der Daten hat mitgeteilt, dass er sie erhalten hat.“

Wie Sie sich denken können, ist der Empfänger der von Ihrem Programm ausgegebenen Daten das Terminal (der Emulator), auf dem Ihr Programm läuft – entweder ein Linux VT oder eine Instanz des GNOME-Terminals in Ihren Tests. (Das Gesamtbild ist komplizierter, da der Kernel die Daten nicht direkt an einen laufenden Terminalemulator sendet, aber wir wollen die Beschreibung nicht verkomplizieren.)

Und daher hängt die Geschwindigkeit, mit der dieser write()Systemaufruf abgeschlossen wird, stark davon ab, wie schnell der Empfänger der Daten damit umgeht! In Ihrem Fall erledigt GNOME Terminal dies einfach viel schneller.

Meiner Ansicht nach besteht der Unterschied darin, dass der VT-Treiber alle an ihn gesendeten Daten gewissenhaft rendert, sie scrollt usw., während GNOME Terminal eingehende Datenschübe optimiert, indem es nur den letzten Teil davon rendert (was auch immer auf die Bildschirmgröße des Terminals passt) und den Rest in den sogenannten „Scroll-Puffer“ legt, über den die meisten GUI-Terminalemulatoren verfügen.

Die Takeaways zu tun

Der entscheidende Punkt, den Sie dabei beachten sollten, ist, dass, wenn Ihr Programm neben Berechnungen auch I/O-Vorgänge durchführt und Sie die Berechnungsgeschwindigkeit des Programms mithilfe eines „Wanduhr“-Timers messen, Sie in der Regel die Geschwindigkeit dieser I/O-Vorgänge messen, nicht die Geschwindigkeit der Berechnungen.

Beachten Sie, dass I/O schwierig ist: Ihr Prozess kannvorweggenommen(gestoppt und seine Ressourcen an einen anderen Prozess übergeben) durch das Betriebssystem, immer wenn es darauf wartet, dass eine E/A-Ressource zum Schreiben verfügbar wird – wie etwa ein Festplattenlaufwerk.

Der sicherste Weg, die "rohe" Leistung von Berechnungen zu messen, besteht darin, in Ihrem Programm eine Möglichkeit zu haben, alle Ein- und Ausgaben zu deaktivieren. Wenn das nicht möglich ist oder die Implementierung zu hässlich wäre, versuchen Sie zumindest, die gesamte Ausgabe auf ein sogenanntes "Nullgerät" umzuleiten, /dev/nullindem Sie Ihr Programm wie folgt ausführen:

$ ./program >/dev/null

Das Nullgerät verwirft einfach alle an es übergebenen Daten. Also ja, jede von der C++-Standardbibliothek ausgeführte E/A-Runde wird den Kernel weiterhin treffen, aber Sie haben zumindest eine nahezu konstante (und fast sofortige) Schreibgeschwindigkeit.

Wenn Sie beide Maßnahmen benötigenUndUm die generierten Daten zu speichern, sollten Sie eine sogenannte RAM-Disk erstellen und die Ausgabe in eine dort befindliche Datei umleiten.

Noch eins zum Messen: Beachten Sie, dass selbst auf einem scheinbar inaktiven System mit einem Standardbetriebssystem (wie Ubuntu oder was auch immer) die CPU nie schläft – im Hintergrund laufen immer einige Aufgaben. Das bedeutet, dass die Messung der Rechenleistung selbst ohne I/O oder mit quasi „deaktiviertem“ I/O (wie oben erläutert) bei jedem Durchlauf zu unterschiedlichen Ergebnissen führt.

Um dies auszugleichen, bedeutet gutes Benchmarking, dass Sie Ihre Berechnung mit denselben Eingabedaten mehrere tausend Mal ausführen und die Ergebnisse über die Anzahl der Durchläufe mitteln.

verwandte Informationen