Geschwindigkeitsvergleich zwischen fgetc / fputc und fread / fwrite in C

8

Also (nur zum Spaß), ich habe gerade versucht, einen C-Code zu schreiben, um eine Datei zu kopieren. Ich lese herum und es scheint, dass alle Funktionen aus einem Stream Aufruf fgetc() (ich hoffe, das ist wahr?) Zu lesen, also habe ich diese Funktion verwendet:

%Vor%

Dies ergab eine Laufzeit von 140 ms für diese Datei auf einem 2.10Ghz core2Duo T6500 Dell Inspiron Laptop. Wenn ich versuche, fread / fwrite zu verwenden, bekomme ich jedoch eine abnehmende Laufzeit, da ich die Anzahl der Bytes (dh Variable st im folgenden Code) für jeden Aufruf so lange erhöht, bis sie bei etwa 10ms einen Spitzenwert erreicht! Hier ist der Code:

%Vor%

Warum passiert das? Wenn zB fread mehrere Aufrufe von fgetc enthält, warum dann die Geschwindigkeitsdifferenz? EDIT: spezifiziert, dass "zunehmende Anzahl von Bytes" sich auf die Variable st im zweiten Code bezieht

    
pratikm 24.11.2011, 00:20
quelle

4 Antworten

15

fread() ruft fgetc() nicht auf, um jedes Byte zu lesen.

Er verhält sich als ob wiederholt fgetc() aufruft, aber er hat direkten Zugriff auf den Puffer, von dem fgetc() liest, so dass er direkt eine größere Menge an Daten kopieren kann.

    
TJD 24.11.2011, 00:23
quelle
6

Sie vergessen die Dateipufferung ( inode, Dentry und Page Caches ).

Löschen Sie sie, bevor Sie fortfahren:

%Vor%

Backgrounder:

Benchmarking ist eine Kunst. Siehe bonnie++ , iozone und phoronix für das korrekte Dateisystem-Benchmarking. Als Merkmal erlaubt bonnie++ keinen Benchmark mit einem geschriebenen Volumen von weniger als dem doppelten verfügbaren Systemspeicher.

Warum?

(Antwort: Puffereffekte!)

    
sehe 24.11.2011 00:23
quelle
2

Wie sehe sagt es teilweise weil puffern, aber da ist mehr dran und ich werde erklären wieso ist das und gleichzeitig warum fgetc() mehr Latenz geben wird.

fgetc() wird für jedes Byte aufgerufen, das aus der Datei gelesen wird.

fread() wird für alle n Bytes des lokalen Puffers für Dateidaten aufgerufen.

Also für eine 10MiB-Datei:

fgetc() heißt: 10 485 760 mal

Während fread mit einem 1KiB-Puffer die Funktion 10 240 mal aufgerufen hat.

Sagen wir der Einfachheit halber, dass jeder Funktionsaufruf 1ms dauert: fgetc würde 10 485 760 ms = 10485.76 Sekunden ~ 2.9127 Stunden dauern fread würde 10 240 ms = 10,24 Sekunden dauern

Obendrein liest und schreibt das Betriebssystem normalerweise dasselbe Gerät. Ich nehme an, Ihr Beispiel macht es auf der gleichen Festplatte. Das Betriebssystem beim Lesen Ihrer Quelldatei, bewegen Sie die Festplattenköpfe über die rotierenden Platten Platten sucht die Datei und liest dann 1 Byte, legen Sie es in den Speicher, und bewegen Sie wieder den Lese- / Schreibkopf über die Festplatten Platten auf der Stelle suchen dass das Betriebssystem und der Festplattencontroller übereingekommen sind, die Zieldatei zu lokalisieren, und dann 1 Byte aus dem Speicher schreibt. Für das obige Beispiel geschieht dies über 10 Millionen Mal für jede Datei: insgesamt mehr als 20 Millionen Mal, mit der gepufferten Version passiert das nur eine Gesamtsumme von über 20 000 mal.

Außerdem speichert das Betriebssystem beim Lesen der Festplatte einige weitere KiB-Festplattendaten für Leistungszwecke, was das Programm beschleunigen kann, auch wenn das weniger effiziente fgetc verwendet wird, weil das Programm aus dem OS-Speicher liest anstatt direkt von der Festplatte zu lesen. Dies bezieht sich auf die Antwort von sehe.

Abhängig von Ihrer Maschinenkonfiguration / Auslastung / Betriebssystem / etc können Ihre Ergebnisse beim Lesen und Schreiben sehr unterschiedlich sein. Daher empfiehlt er, die Festplattencaches zu leeren, um bessere Ergebnisse zu erzielen.

Wenn Quell- und Zieldateien auf einer anderen Festplatte sind, sind die Dinge viel schneller. Mit SDDs bin ich nicht wirklich sicher, ob Lesen / Schreiben sich absolut exklusiv sind.

Zusammenfassung: Jeder Aufruf einer Funktion hat einen gewissen Overhead, das Lesen von einer Festplatte hat andere Gemeinkosten und Caches / Puffer helfen, Dinge schneller zu machen.

Weitere Informationen

  

Ссылка

     

Ссылка

    
RedComet 24.11.2011 03:24
quelle
2

stdio-Funktionen füllen einen Lesepuffer der Größe "BUFSIZ" wie in stdio.h definiert und führen nur jedes Mal einen Lese- (2) -Systemaufruf aus, wenn der Puffer leer ist. Sie werden keinen individuellen Lese (2) -Systemaufruf für jedes verbrauchte Byte ausführen - sie lesen große Teile. BUFSIZ ist typischerweise so etwas wie 1024 oder 4096.

Sie können auch die Größe dieses Puffers anpassen, wenn Sie möchten, um ihn zu erhöhen - sehen Sie die man-Seiten für setbuf / setvbuf / setbuffer auf den meisten Systemen - obwohl das wahrscheinlich keinen großen Unterschied in der Leistung macht.

>

Andererseits können Sie, wie Sie bemerken, einen Systemaufruf vom Typ read (2) beliebiger Größe machen, indem Sie diese Größe im Aufruf festlegen, auch wenn Sie zu diesem Zeitpunkt immer weniger davon erhalten.

Übrigens, Sie könnten auch open (2) und nicht fopen (3) verwenden, wenn Sie die Dinge so machen. Es hat wenig Sinn, eine Datei zu öffnen, die Sie nur für ihren Dateideskriptor verwenden wollen.

    
Perry 28.02.2012 00:07
quelle

Tags und Links