2009-04-20 9 views
12

Ich habe diese Funktion in C unter Verwendung von Systemaufrufen erfolgen (öffnen, lesen und schreiben), um die „cat“ Funktion in Linux-Systeme zu simulieren, und es ist langsamer als die reale ...Warum funktioniert meine "Katze" mit Systemaufrufen langsamer als die "Katze" von Linux?

Ich bin mit dem gleichen Puffergröße als die echte "Katze" und mit "strace" Ich denke, es macht die gleiche Menge an Systemaufrufen. Aber die Ausgabe von meiner "Katze" ist ein bisschen langsamer als die echte "Katze".

Dies ist der Code, den ich habe:

#define BUFSIZ 32768 

int sysWriteBuffer(int fdout, char *buffer, ssize_t readBytes) { 
    ssize_t writtenBytes = 0; 

    while(writtenBytes < readBytes) { 
     writtenBytes += write(fdout, 
      buffer + writtenBytes, readBytes - writtenBytes); 
     if(writtenBytes == -1) { 
      return -1; 
     } 
    } 

    return 0; 
} 

int catPrint(int fdin, int fdout) { 
    char buffer[BUFSIZ]; 
    ssize_t readBytes; 

    do { 
     readBytes = read(fdin, buffer, BUFSIZ); 

     if(readBytes == -1) { 
      return -1; 
     } 

     if(sysWriteBuffer(fdout, buffer, readBytes) == -1) { 
      return -1; 
     } 
    } while(readBytes > 0); 

    return 0; 
} 

Ich bin aus einer Datei zu lesen (die ich als Argument zur Haupt passieren, denke ich, dass Code hier nicht benötigt wird), als ich die CatPrint() aufrufen Funktion mit diesem Dateideskriptor und 1 für den Ausgabedeskriptor, so dass es nach stdout druckt.

Ich verstehe nicht, warum es langsamer ist, weil ich die gleiche Datei zum Testen verwende und mit beiden (dem echten "Katze" und mir) gibt es nur einen read() und einen write() für den ganzen Text. Sollte nicht der ganze Text auf dem Bildschirm erscheinen?

P.S: Ich habe dies als Hausaufgabe markiert, obwohl meine Frage hier (warum es langsamer ist) ist nicht Teil der Hausaufgaben. Ich musste nur die Systemaufrufe verwenden, um eine "cat" -Funktion zu erstellen, was erledigt ist. Ich bin nur fasziniert von meinem Code, der ein bisschen langsamer ist.

PROBLEM MIT STUPIDITY VON MIR GELÖST:
Ich habe gerade beschlossen, Linux ursprüngliche Katze ein paar Mal auf der gleichen Datei zu nennen, eine nach der anderen, und ich erkannte, dass es auch einige der Zeit langsam war ich nannte es so langsam wie mein eigenes. Ich denke, alles ist in Ordnung als ...

Entschuldigung für die Verschwendung Ihrer Zeit so Leute.

+1

IMHO, das Hausaufgaben-Tag ist irreführend. Ihre Frage betrifft einen interessanten Hintergrund. "Hausaufgaben" impliziert entweder mühsame Anfängerarbeit oder (am anderen Ende der Skala) eine Quizfrage. –

+0

BTW der Fehler (dh write returning -1) Behandlung ist falsch, wenn der Fehler beim zweiten write() passiert. – jpalecek

+0

Sie können das Hausaufgaben-Tag löschen, wenn Sie denken, es ist besser ... Was meinst du jpalecek? Es gibt nur einen Schreibzugriff (wie im Systemaufruf) Ich habe nur eine Hilfsfunktion. Wenn das write() innerhalb dieser Hilfsfunktion fehlschlägt, muss ich den -1 vollständig zurückgeben, wo catPrint() aufgerufen wurde ... –

Antwort

15

Ah, basierend auf Ihrer Bearbeitung wurden Sie vom readahead Puffer gebissen. Sie können nicht zwei Programme testen, die Dateien nebeneinander lesen, indem Sie sie einmal ausführen. Die erste ist immer langsamer, da sich die Datei auf der Festplatte befindet. Sobald die Datei im Speicher ist, wird die zweite schneller ausgeführt. Sie müssen entweder neue Daten für jede Datei erstellen oder eine ausführen und beide ausführen, damit beide den Vorteil des Readahead-Puffers erhalten.

1

Wie viel? Die kanonische Katze ist so etwas wie

char bufr[BUFSIZ]; 
ssize_t len; 

while((len=read(fdin, bufr, BUFSIZ)) >0) 
    write(fdout, bufr, len); 

, die ein paar Anweisungen speichert.

+0

Dies kann die kanonische Version sein, aber eine inkorrekte Version (zB wenn ein Signal kommt, während Sie schreiben()) – jpalecek

+0

Welchen Teil von "sowas" haben Sie vermisst? –

+0

Wie ich schon sagte, cat und meine Katze, beide nennen eine read() mit einer Puffergröße von 32768 und eine write() mit der gleichen Puffergröße und eine letzte read() am Ende (wenn es nichts liest sonst und endet). –

3

Vielleicht haben Sie ohne Optimierung kompiliert (oder ohne eine so hohe Optimierungseinstellung)?

Auch wird Ihr Code sysWriteBuffer einmal mit readBytes gleich Null anrufen - vielleicht das (teilweise) erklärt es?

Sie können auch sysWriteBuffer inline (entweder über einen Compiler oder von Hand).

"inlining" bedeutet, den Körper einer Funktion zu seiner Aufrufstelle zu kopieren, um den Overhead des Aufrufs einer Funktion zu entfernen. Manchmal machen Compiler das automatisch (ich denke, -O3 aktiviert diese Optimierung in gcc). Sie können auch das Schlüsselwort inline in gcc verwenden, um dem Compiler mitzuteilen, dass eine Funktion inline ist. Wenn Sie dies tun, wird Ihre Erklärung wie folgt aussehen:

static inline int sysWriteBuffer(int fdout, char *buffer, ssize_t readBytes) { 
.... 
+0

Wenn Sie strace auf Katze verwenden, werden Sie sehen, dass es dort auch passiert, also habe ich es einfach verlassen ... Und ich verwende die -O2-Flagge. –

+0

Sie könnten versuchen "-O3 -funroll_loops" und sehen, wie das geht. Besser wäre es noch, die genauen Flags zu ermitteln, mit denen cat kompiliert wurde. –

+0

Nur eine Anmerkung, die Klappe ist -Ferroll-Loops (zweiter Bindestrich kein Unterstrich), und ich denke nicht, dass es in diesem Fall eine ganze Menge tun wird. – Anthony

1

Haben Sie vergleichen strace s von beiden? Sie könnten versuchen, den -tt Parameter zu verwenden, damit Sie das Timing der Systemaufrufe erhalten.

+0

Mein Wissen über Strace ist nicht viel und ich versuchte, die -tt Parameter und Haufen von Zahlen erschienen, aber ich kann ihre Bedeutung nicht verstehen. –

+0

Versuchen Sie, das Lesen und Schreiben des Teils zu finden (die Ausgabe sollte das Format "time syscall (parameters) = return value" haben, also lese() oder write()) und es posten – jpalecek

3

Forschung mmap (2).

Sie werden immer die Feinheiten von ftell/fread werfen, aber es wird eine Ebene der Indirektion überspringen, wenn der Lesedurchsatz wirklich wichtig ist.

+0

Danke, ich habe diese mmap benötigt. –

+0

Ich darf nichts anderes für diese Übung verwenden. –

2

Ohne Vergleich der Quellcodes ist es schwer zu sagen. Wenn Sie Ihre Katze mit einer GNU-Katze vergleichen, denken Sie daran, dass Sie einen Code, der einige Stunden/Tage alt ist, mit einem Code vergleichen, der sich über mehr als zwanzig Jahre entwickelt hat.

Sie können eine umfassendere Leistungsanalyse durchführen, indem Sie beide Programme mit unterschiedlichen Eingangsgrößen ausführen, von verschiedenen Geräten (eine RAM-Disk wäre gut) und mehrere Male hintereinander. Sie müssen versuchen, WHERE in Ihrem Programm zu bestimmen, es ist langsamer.

Da Katze selbst ist wirklich trivial (und Sie sagten in einem Kommentar, dass Sie bereits die Kompilierung optimieren), ich wette, die Leistung Auswirkungen, die Sie beobachten, ist nicht im eigentlichen Algorithmus, sondern auf Programmladezeiten. Wenn das Systembinär prelinked ist (was heutzutage in den meisten Distributionen üblich ist), werden Sie sehen, dass es schneller geladen wird als jedes Programm, das Sie selbst kompilieren (bis Sie Ihre Programme in die Verknüpfung einbeziehen).