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!

35voto

qrdl Punkte 32507

Die ungepufferte Ausgabe ist sehr langsam.

Standardmäßig stdout ist jedoch vollständig gepuffert, wenn es an ein Terminal angeschlossen ist, stdout ist entweder ungepuffert oder zeilengepuffert.

Versuchen Sie, die Pufferung einzuschalten für stdout mit setvbuf() etwa so:

char buffer[8192];

setvbuf(stdout, buffer, _IOFBF, sizeof(buffer));

15voto

edoloughlin Punkte 5623

Sie könnten Ihre Zeichenketten in einem Puffer speichern und sie am Ende oder in regelmäßigen Abständen, wenn der Puffer voll ist, in eine Datei (oder die Konsole) ausgeben.

Bei der Ausgabe auf einer Konsole ist das Scrollen in der Regel ein Killer.

9voto

Thomas Bonini Punkte 42356

Wenn Sie printf()ing auf der Konsole ausgeben, ist das normalerweise extrem langsam. Ich bin mir nicht sicher, warum, aber ich glaube, es kehrt nicht zurück, bis die Konsole die ausgegebene Zeichenkette grafisch anzeigt. Außerdem kann man mmap() nicht auf stdout anwenden.

Das Schreiben in eine Datei sollte viel schneller sein (aber immer noch um Größenordnungen langsamer als das Berechnen eines Hashes, alle E/A ist langsam).

7voto

corvus Punkte 2280

Sie können versuchen, die Ausgabe in der Shell von der Konsole in eine Datei umzuleiten. Auf diese Weise können Protokolle mit einer Größe von Gigabytes in wenigen Sekunden erstellt werden.

7voto

n00dle Punkte 5782
  1. E/A ist immer langsam im Vergleich zu reinen Berechnungen. Das System muss warten, bis mehr Komponenten verfügbar sind zur Verfügung stehen, um sie zu nutzen. Es muss dann auf die Antwort warten warten, bevor es weitermachen kann. Umgekehrt wenn er einfach nur rechnet, dann bewegt er wirklich nur Daten zwischen den RAM und CPU-Registern.

  2. Ich habe dies nicht getestet, aber es kann schneller sein, um Ihre Hashes auf eine Zeichenfolge anhängen, und dann nur die Zeichenfolge am Ende drucken. Obwohl, wenn Sie C, nicht C++ verwenden, kann dies ein Schmerz zu sein!

3 und 4 sind für mich leider unerreichbar.

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