29 Stimmen

printf verlangsamt mein Programm

Ich habe ein kleines C-Programm zur Berechnung von Hashes (für Hashtabellen). Der Code sieht ziemlich sauber aus, hoffe ich, aber es gibt etwas, das damit nicht zusammenhängt und mich stört.

Ich kann problemlos etwa eine Million Hashes in etwa 0,2-0,3 Sekunden erzeugen (gemessen mit /usr/bin/time). Wenn ich sie jedoch in der for-Schleife mit printf() ausdrucke, verlangsamt sich das Programm auf etwa 5 Sekunden.

  1. Warum ist das so?
  2. Wie kann man es schneller machen? mmapp()ing stdout vielleicht?
  3. Wie ist stdlibc in dieser Hinsicht konzipiert, und wie kann sie verbessert werden?
  4. Wie könnte der Kernel dies besser unterstützen? Wie müsste er modifiziert werden, damit der Durchsatz bei lokalen "Dateien" (Sockets, Pipes usw.) WIRKLICH schnell wird?

Ich freue mich auf interessante und ausführliche Antworten. Danke!

PS: Dies ist für ein Compilerbau-Toolset, also scheuen Sie sich nicht, ins Detail zu gehen. Das hat zwar nichts mit dem eigentlichen Problem zu tun, aber ich wollte nur darauf hinweisen, dass mich Details interessieren.

Nachtrag

Ich bin auf der Suche nach mehr programmatischen Ansätzen für Lösungen und Erklärungen. Piping erfüllt zwar die Aufgabe, aber ich habe keine Kontrolle darüber, was der "Benutzer" tut.

Natürlich führe ich gerade einen Test durch, der von "normalen Benutzern" nicht durchgeführt werden würde. ABER das ändert nichts an der Tatsache, dass ein einfaches printf() einen Prozess verlangsamt, und das ist das Problem, für das ich versuche, eine optimale programmatische Lösung zu finden.


Addendum - Erstaunliche Ergebnisse

Die Referenzzeit ist für einfache printf()-Aufrufe innerhalb eines TTY und dauert etwa 4 Minuten 20 Sekunden.

Das Testen unter einem /dev/pts (z.B. Konsole) beschleunigt die Ausgabe auf etwa 5 Sekunden.

Es dauert ungefähr gleich lange, wenn ich setbuffer() in meinem Testcode für eine Größe von 16384 verwende, und fast genauso lange für 8192: ungefähr 6 Sekunden.

setbuffer() hat offenbar keine Auswirkung bei der Benutzung: es dauert gleich lang (auf einem TTY etwa 4 Minuten, auf einem PTS etwa 5 Sekunden).

Das Erstaunliche dabei ist wenn ich den Test auf TTY1 starte und dann zu einem anderen TTY wechseln dauert es genauso lange wie bei einem PTS: etwa 5 Sekunden.

Schlussfolgerung : Der Kernel macht etwas, das mit Zugänglichkeit und Benutzerfreundlichkeit zu tun hat. HUH!

Normalerweise sollte es gleich langsam sein, egal ob Sie das TTY anstarren, während es aktiv ist, oder ob Sie zu einem anderen TTY wechseln.


Lektion : Wechseln Sie bei ausgabeintensiven Programmen auf einen anderen TTY!

4voto

AnthonyLambert Punkte 8455
  1. Warum werden die Saiten nicht bei Bedarf erstellt, sondern erst bei der Konstruktion? Es macht keinen Sinn, 40 Bildschirme mit Daten in einer Sekunde auszugeben, wie soll man sie dann lesen? Warum wird die Ausgabe nicht nach Bedarf erstellt und nur der letzte Bildschirm voll angezeigt und dann nach Bedarf, wenn der Benutzer scrollt?

  2. Warum nicht sprintf verwenden, um in eine Zeichenkette zu drucken und dann eine verkettete Zeichenkette mit allen Ergebnissen im Speicher zu erstellen und am Ende zu drucken?

  3. Wenn Sie zu sprintf wechseln, können Sie deutlich sehen, wie viel Zeit für die Formatkonvertierung und wie viel für die Anzeige des Ergebnisses auf der Konsole aufgewendet wird, und den Code entsprechend ändern.

  4. Die Konsolenausgabe ist per definitionem langsam, die Erstellung eines Hashes ist nur eine Manipulation von ein paar Bytes Speicher. Die Konsolenausgabe muss viele Schichten des Betriebssystems durchlaufen, das über Code zur Behandlung von Threads/Prozesssperren usw. verfügt, bis sie schließlich den Anzeigetreiber erreicht, der vielleicht ein 9600-Baud-Gerät! oder eine große Bitmap-Anzeige ist, wobei einfache Funktionen wie das Scrollen des Bildschirms die Bearbeitung von Megabytes an Speicher erfordern können.

4voto

Marco Punkte 2593

Da E/A immer viel langsamer ist als CPU-Berechnungen, können Sie alle Werte zuerst in der schnellstmöglichen E/A speichern. Verwenden Sie also RAM, wenn Sie genug haben, und Dateien, wenn nicht, aber das ist viel langsamer als RAM.

Das Ausdrucken der Werte kann nun nachträglich oder parallel durch einen anderen Thread erfolgen. Der/die Berechnungs-Thread(s) brauchen also nicht zu warten, bis printf zurückgekehrt ist.

4voto

Mike Dunlavey Punkte 39339

Ich habe vor langer Zeit entdeckt mit dieser Technik etwas, das eigentlich offensichtlich hätte sein müssen. Die E/A ist nicht nur langsam, vor allem für die Konsole, sondern die Formatierung von Dezimalzahlen ist auch nicht schnell. Wenn Sie die Zahlen in Binärform in große Puffer legen und diese in eine Datei schreiben können, werden Sie feststellen, dass es viel schneller geht.

Außerdem, wer wird sie schon lesen? Es macht keinen Sinn, sie alle in einem für Menschen lesbaren Format zu drucken, wenn niemand sie alle lesen muss.

2voto

t0mm13b Punkte 33393

Ich vermute, dass der Terminaltyp einige gepufferte Ausgabeoperationen verwendet. Wenn Sie also ein printf ausführen, wird es nicht in Bruchteilen von Mikrosekunden ausgegeben, sondern im Pufferspeicher des Terminal-Subsystems gespeichert.

Dies könnte durch andere Dinge beeinträchtigt werden, die eine Verlangsamung verursachen könnten, z. B. wenn neben Ihrem Programm noch eine speicherintensive Operation auf dem Rechner läuft. Kurz gesagt, es gibt viel zu viele Dinge, die alle gleichzeitig passieren könnten, Paging, Swapping, starke E/A durch einen anderen Prozess, die Konfiguration des verwendeten Speichers, vielleicht eine Speicheraufrüstung und so weiter.

Es könnte besser sein, die Zeichenketten zu verketten, bis eine bestimmte Grenze erreicht ist, und dann alles auf einmal auszuschreiben, wenn dies der Fall ist. Oder sogar pthreads zu verwenden, um die gewünschte Prozessausführung durchzuführen.

Bearbeitet: Was 2,3 betrifft, so verstehe ich das nicht. Für 4, Ich bin mit Sun nicht vertraut, aber ich kenne Solaris und habe mich damit beschäftigt, Vielleicht gibt es eine Kernel-Option, um ein virtuelles TTY zu verwenden. Ich gebe zu, es ist eine Weile her, seit ich mit den Kernel-Konfigurationen herumgespielt und ihn neu kompiliert habe. Daher kann es sein, dass mein Gedächtnis nicht mehr ganz so gut ist, probier es mal mit den Optionen.

user@host:/usr/src/linux $ make; make menuconfig \*\*OR kconfig if from X\*\*

Dadurch wird das Kernel-Menü aufgerufen. Schauen Sie sich den Abschnitt Videoeinstellungen unter dem Unterbaum Geräte an.

Bearbeitet: aber es gibt eine Änderung, die man in den Kernel einfügt, indem man eine Datei in das proc-Dateisystem hinzufügt (falls es so etwas gibt), oder möglicherweise einen Schalter, der in den Kernel eingegeben wird, etwa so (das ist fantasievoll und bedeutet nicht, dass es tatsächlich existiert), fastio

Ich hoffe, das hilft, Mit freundlichen Grüßen, Tom.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X