printf verlangsamt mein Programm

Lesezeit: 6 Minuten

Benutzer-Avatar
Flavius

Ich habe ein kleines C-Programm zum Berechnen von Hashes (für Hash-Tabellen). Der Code sieht ziemlich sauber aus, hoffe ich, aber es gibt etwas, das nichts damit zu tun hat, was mich nervt.

Ich kann leicht ungefähr eine Million Hashes in ungefähr 0,2-0,3 Sekunden generieren (benchmarked mit /usr/bin/time). Wenn ich sie jedoch in der for-Schleife ausdrucke, verlangsamt sich das Programm auf etwa 5 Sekunden.

  1. Warum ist das?
  2. Wie macht man es schneller? mmapp() vielleicht stdout?
  3. Wie ist die stdlibc diesbezüglich gestaltet und wie kann sie verbessert werden?
  4. Wie könnte der Kernel es besser unterstützen? Wie müsste es geändert werden, um den Durchsatz für lokale “Dateien” (Sockets, Pipes usw.) WIRKLICH schnell zu machen?

Ich freue mich auf interessante und ausführliche Antworten. Vielen Dank.

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

Nachtrag

Ich suche nach mehr programmatischen Ansätzen für Lösungen und Erklärungen. In der Tat erledigt die Verrohrung die Arbeit, aber ich habe keine Kontrolle darüber, was der “Benutzer” tut.

Natürlich mache ich gerade einen Test, den “normale User” nicht machen würden. ABER das ändert nichts an der Tatsache, dass ein einfaches printf() einen Prozess verlangsamt, was das Problem ist, für das ich versuche, eine optimale programmatische Lösung zu finden.


Nachtrag – Erstaunliche Ergebnisse

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

Testen unter /dev/pts (zB Konsole) beschleunigt die Ausgabe auf ca. 5 Sekunden.

Es dauert ungefähr die gleiche Zeit, wenn ich setbuffer() in meinem Testcode auf eine Größe von 16384 verwende, fast die gleiche für 8192: ungefähr 6 Sekunden.

setbuffer() hat offenbar keine Auswirkung bei der Verwendung: Es dauert die gleiche Zeit (auf einem TTY etwa 4 Minuten, auf einem PTS etwa 5 Sekunden).

Das Erstaunliche istwenn ich den Test auf TTY1 starte und dann Wechseln Sie zu einem anderen TTYes dauert genauso wie auf einem PTS: ca. 5 Sekunden.

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

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


Lektion: bei ausgabeintensiven Programmen auf einen anderen TTY umschalten!

  • Wenn Sie die Ausgabe nach /dev/null umleiten, wie schnell ist Ihr Programm dann?

    – Erich Kitzmüller

    2. Dezember 2009 um 12:22 Uhr

  • @ammoQ: Genauso schnell wie beim Umleiten auf eine normale Datei: etwa 0,5 Sekunden.

    – Flavius

    2. Dezember 2009 um 12:41 Uhr

  • Es ist keine “einfache” Angelegenheit. I/O ist im Allgemeinen um Größenordnungen langsamer als reine CPU-Berechnungen und Busoperationen, es sollte nicht so erstaunlich sein, dies zu erkennen.

    – Michael Foukarakis

    2. Dezember 2009 um 13:33 Uhr

  • Es ist erstaunlich, dass, wenn Sie sich das TTY ansehen, während der Prozess ausgeführt wird und etwas anzeigt, die Ausführung 4 Minuten dauert. Wenn Sie nicht auf das TTY schauen, dauert es 5 Sekunden.

    – Flavius

    2. Dezember 2009 um 14:17 Uhr

  • Flavius: Das liegt daran, dass bei der Anzeige von TTY jede neue Zeile das Scrollen des gesamten Bildschirms nach oben erfordert. Jede Zeichenzelle auf dem Bildschirm wird einem bestimmten Ort im Bildschirmpuffer zugeordnet. Das Verschieben von Zeichen bedeutet also, dass Bytes im Bildschirmpuffer verschoben werden. Auf einer Konsole mit 80 Spalten bedeutet das, dass das Verschieben von 24 Zeilen nach oben im Wesentlichen a ist memmove von fast 2k – was erledigt ist für jede Zeile, die Sie ausgeben.

    – Café

    2. Dezember 2009 um 23:04 Uhr

Benutzer-Avatar
qrdl

Die ungepufferte Ausgabe ist sehr langsam.

Standardmäßig stdout ist vollständig gepuffert, aber wenn an das Terminal angeschlossen, stdout ist entweder ungepuffert oder zeilengepuffert.

Versuchen Sie, die Pufferung für einzuschalten stdout verwenden setvbuf()so was:

char buffer[8192];

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

  • Oh, printf() schreibt standardmäßig nach stdout. Ich greife nicht in die Funktionsweise von printf() ein.

    – Flavius

    2. Dezember 2009 um 12:37 Uhr

Benutzer-Avatar
edoloughlin

Sie könnten Ihre Zeichenfolgen in einem Puffer speichern und sie am Ende oder regelmäßig, wenn Ihr Puffer voll ist, in eine Datei (oder Konsole) ausgeben.

Bei der Ausgabe an eine Konsole ist das Scrollen normalerweise ein Killer.

  • +1, besonders zum Scrollen. Stellen Sie sich nur all das Blitting und Bitmap-Kopieren vor, das mit dem Scrollen verbunden ist …

    – schleske

    2. Dezember 2009 um 12:24 Uhr

  • Ihre Antwort veranlasste mich, das Programm unter einem sauberen TTY und unter einem verwalteten PTS von Konsole zu testen. Das Ergebnis: Konsole beschleunigt die Dinge erheblich! Es dauerte 4 Minuten und 20 Sekunden, wenn es von TTY aus lief (das als wahre Referenz für den Test verwendet werden sollte, denke ich), 5 Sekunden von PTY.

    – Flavius

    2. Dezember 2009 um 12:27 Uhr

  • ein weiteres +1 zum Scrollen. Einfach ein gesprächiges Programm im GNU-Bildschirm auszuführen (und es dann zu trennen) würde die Dinge erheblich beschleunigen!

    – Lester Cheung

    7. Januar 2011 um 4:03 Uhr

Wenn Sie auf die Konsole drucken, ist es normalerweise extrem langsam. Ich bin mir nicht sicher warum, aber ich glaube, es kehrt nicht zurück, bis die Konsole die ausgegebene Zeichenfolge grafisch anzeigt. Außerdem können Sie nicht mmap() zu stdout.

Das Schreiben in eine Datei sollte viel schneller sein (aber immer noch um Größenordnungen langsamer als das Berechnen eines Hashs, alle I/Os sind langsam).

Sie können versuchen, die Ausgabe in der Shell von der Konsole in eine Datei umzuleiten. Damit lassen sich in Sekundenschnelle Protokolle mit einer Größe von Gigabyte erstellen.

Benutzer-Avatar
n00dle

  1. I/O ist im Vergleich zur direkten Berechnung immer langsam. Das System muss warten, bis weitere Komponenten verfügbar sind, um sie verwenden zu können. Es muss dann auf die Antwort warten, bevor es weitermachen kann. Umgekehrt, wenn es sich nur um Berechnungen handelt, werden nur Daten zwischen den RAM- und CPU-Registern verschoben.

  2. Ich habe das nicht getestet, aber es kann schneller sein, Ihre Hashes an eine Zeichenfolge anzuhängen und die Zeichenfolge dann einfach am Ende auszugeben. Wenn Sie C und nicht C++ verwenden, kann sich dies jedoch als mühsam erweisen!

3 und 4 sind mir schleierhaft, fürchte ich.

Benutzer-Avatar
Marco

Da E/A immer viel langsamer ist als die CPU-Berechnung, können Sie alle Werte zuerst in schnellstmöglicher E/A speichern. Verwenden Sie also RAM, wenn Sie genug haben, verwenden Sie Dateien, wenn nicht, aber es ist viel langsamer als RAM.

Das Ausdrucken der Werte kann nun nachträglich oder parallel von einem anderen Thread erfolgen. Daher müssen die Berechnungsthreads möglicherweise nicht warten, bis printf zurückgekehrt ist.

Benutzer-Avatar
Gemeinschaft

Ich habe vor langer Zeit mit dieser Technik etwas entdeckt, das offensichtlich hätte sein sollen. E/A ist nicht nur langsam, insbesondere zur Konsole, auch das Formatieren von Dezimalzahlen ist 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 lesen? Es hat keinen Sinn, sie alle in einem für Menschen lesbaren Format auszudrucken, wenn niemand sie alle lesen muss.

1372740cookie-checkprintf verlangsamt mein Programm

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy