2016-08-09 9 views
6

Ich schrieb eine Funktion, die zwei große Dateien (file1,file2) in eine neue Datei (outputFile) zusammenführt. Jede Datei ist ein zeilenbasiertes Format, während die Einträge durch \ 0 Byte getrennt sind. Beide Dateien haben die gleiche Anzahl von Nullbytes.Verbesserung der IO-Performance zum Zusammenführen zweier Dateien in C

Ein Beispiel mit zwei Einträgen Datei wie folgt A\nB\n\0C\nZ\nB\n\0

Input: 
    file1: A\nB\0C\nZ\nB\n\0 
    file2: BBA\nAB\0T\nASDF\nQ\n\0 
    Output 
    outputFile: A\nB\nBBA\nAB\0C\nZ\nB\nT\nASDF\nQ\n\0 

FILE * outputFile = fopen(...); 
setvbuf (outputFile , NULL , _IOFBF , 1024*1024*1024) 
FILE * file1 = fopen(...); 
FILE * file2 = fopen(...); 
int c1, c2; 
while((c1=fgetc(file1)) != EOF) { 
    if(c1 == '\0'){ 
     while((c2=fgetc(file2)) != EOF && c2 != '\0') { 
      fwrite(&c2, sizeof(char), 1, outputFile); 
     } 
     char nullByte = '\0'; 
     fwrite(&nullByte, sizeof(char), 1, outputFile); 
    }else{ 
     fwrite(&c1, sizeof(char), 1, outputFile); 
    } 
} 

aussehen könnte Gibt es eine Möglichkeit, diese IO Leistung dieser Funktion zu verbessern? Ich erhöhte die Puffergröße von outputFile auf 1 GB mit setvbuf. Wäre es hilfreich, posix_fadvise für Datei1 und Datei2 zu verwenden?

+2

Ich würde zwei Dinge sagen. Zuerst dachte ich, dass 'write' schneller ist als' fwrite'. Zweitens, schreibe nicht jedes einzelne Byte in eine Datei. Machen Sie Ihren eigenen Zwischenpuffer und schreiben Sie große Datenmengen in die Datei. – GMichael

+0

Machen Sie Ihren Code lesbar. – Inline

+0

@GMichael danke für deinen Rat. fwrite sollte gepuffert werden. Denkst du, dass die Funktion, die sich selbst nennt, kritisch ist? –

Antwort

1

Sie tun IO Zeichen-für-Zeichen. Das wird unnötig und schmerzhaft S-L-O-W, sogar mit gepufferten Streams.

Nutzen Sie die Tatsache, dass Ihre Daten in Ihren Dateien als NUL-terminierte Strings gespeichert sind.

Angenommen, Sie sind abwechselnd nul terminierten Strings aus jeder Datei, und auf einer POSIX-Plattform ausgeführt wird, so können Sie einfach mmap() die Eingabedateien:

typedef struct mapdata 
{ 
    const char *ptr; 
    size_t bytes; 
} mapdata_t; 

mapdata_t mapFile(const char *filename) 
{ 
    mapdata_t data; 
    struct stat sb; 

    int fd = open(filename, O_RDONLY); 
    fstat(fd, &sb); 

    data.bytes = sb.st_size; 

    /* assumes we have a NUL byte after the file data 
     If the size of the file is an exact multiple of the 
     page size, we won't have the terminating NUL byte! */ 
    data.ptr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 
    close(fd); 
    return(data); 
} 

void unmapFile(mapdata_t data) 
{ 
    munmap(data.ptr, data.bytes); 
} 

void mergeFiles(const char *file1, const char *file2, const char *output) 
{ 
    char zeroByte = '\0'; 

    mapdata_t data1 = mapFile(file1); 
    mapdata_t data2 = mapFile(file2); 

    size_t strOffset1 = 0UL; 
    size_t strOffset2 = 0UL; 

    /* get a page-aligned buffer - a 64kB alignment should work */ 
    char *iobuffer = memalign(64UL * 1024UL, 1024UL * 1024UL); 

    /* memset the buffer to ensure the virtual mappings exist */ 
    memset(iobuffer, 0, 1024UL * 1024UL); 

    /* use of direct IO should reduce memory pressure - the 1 MB 
     buffer is already pretty large, and since we're not seeking 
     the page cache is really only slowing things down */ 
    int fd = open(output, O_RDWR | O_TRUNC | O_CREAT | O_DIRECT, 0644); 

    FILE *outputfile = fdopen(fd, "wb"); 
    setvbuf(outputfile, iobuffer, _IOFBF, 1024UL * 1024UL); 

    /* loop until we reach the end of either mapped file */ 
    for (;;) 
    { 
     fputs(data1.ptr + strOffset1, outputfile); 
     fwrite(&zeroByte, 1, 1, outputfile); 

     fputs(data2.ptr + strOffset2, outputfile); 
     fwrite(&zeroByte, 1, 1, outputfile); 

     /* skip over the string, assuming there's one NUL 
      byte in between strings */ 
     strOffset1 += 1 + strlen(data1.ptr + strOffset1); 
     strOffset2 += 1 + strlen(data2.ptr + strOffset2); 

     /* if either offset is too big, end the loop */ 
     if ((strOffset1 >= data1.bytes) || 
      (strOffset2 >= data2.bytes)) 
     { 
      break; 
     } 
    } 

    fclose(outputfile); 

    unmapFile(data1); 
    unmapFile(data2);  
} 

ich in keinem Fehler gesetzt haben überhaupt zu prüfen. Sie müssen auch die richtigen Header-Dateien hinzufügen.

Beachten Sie auch, dass die Dateidaten zu NOT ein genaues Vielfaches der Systemseitengröße angenommen werden, wodurch sichergestellt wird, dass ein NUL-Byte nach dem Dateiinhalt zugeordnet ist. Wenn die Größe der Datei ein genaues Vielfaches der Seitengröße ist, müssen Sie mmap() eine zusätzliche Seite nach dem Dateiinhalt, um sicherzustellen, dass es ein NULL-Byte gibt, um die letzte Zeichenfolge zu beenden.

Oder Sie können sich darauf verlassen, dass ein NUL-Byte als letztes Byte des Dateiinhalts vorhanden ist. Wenn sich herausstellt, dass dies nicht der Fall ist, erhalten Sie wahrscheinlich entweder eine SEGV oder beschädigte Daten.

+0

- 1 für die Verwendung von POSIX-spezifischen 'mmap'. Die Frage ruft nicht dafür auf. – rubenvb

+1

@rubenvb Wirklich? Ist es wirklich so schwer für dich, 'mmap()' durch etwas wie Windows 'CreateFileMapping()' zu ersetzen? Es ist sogar in einem separaten Satz von Funktionen gekapselt, um es einfach zu machen. Hier ist ein Link, wenn Sie Hilfe benötigen: https://msdn.microsoft.com/en-us/library/windows/desktop/aa366537%28v=vs.85%29.aspx –

+0

@rubenvb: Und selbst wenn es nicht war Eine Alternative für Nicht-Posix-Systeme ... Posix-Systeme sind immer noch ein wichtiger Anwendungsfall. – Hurkyl

-2

Wenn Sie Threads verwenden können, erstellen Sie einen für file1 und einen anderen für file2.

Machen Sie die outputFile so groß wie Sie brauchen, dann machen Sie thread1 schreiben Sie die Datei1 in outputFile.

Während thread2 suchen es ist Ausgabe von outputFile der die Länge von Datei1 + 1, und schreiben file2

Edit:

Es ist keine richtige Antwort für diesen Fall, aber Verwirrungen ich zu verhindern Ich werde es hier lassen.

Mehr discusion ich es gefunden: improve performance in file IO in C

+0

Threading verbessert nicht wirklich IO. Weil IO die langsamste Operation ist und sequenziell. Sie können IO auf demselben Datenträger nicht parallelisieren. – bolov

+0

Nun, wenn wir nicht blockierende Byte-Datei haben, sollten wir in der Lage sein, es zu schreiben. Es ist das richtig? – Raskayu

+0

Ich bin kein Experte hier, aber ich bin ziemlich zuversichtlich, dass der Festplattenzugriff sequenziell ist, unabhängig davon, auf welche Sektoren Sie zugreifen. Die Beschleunigung wird durch Verwendung von Betriebssystempuffern, Speicherabbilddateien usw. erreicht, nicht durch Lesen/Schreiben in mehreren Threads. – bolov

-1

Eine kleine Verbesserung wäre, dass wenn Sie einzelne Zeichen schreiben, sollten Sie fputc statt fwrite verwenden.

Da außerdem du auf Geschwindigkeit, sollten Sie versuchen, putc und getc statt fputc und fgetc zu sehen, ob es schneller läuft.

0
  • Sie zwei Funktionsaufrufe pro Zeichen (eine für Eingang, einen für den Ausgang) Funktionsaufrufe langsam sind (sie verschmutzen die Befehlspipeline)
  • fgetc() und ihre getc fputc haben()/putc() Gegenstücke, die als Makros implementiert werden (können) werden können und es dem Compiler ermöglichen, die gesamte Schleife mit Ausnahme des Lesens/Schreibens von Puffern zweimal pro 512 oder 1024 oder 4096 Zeichen zu verarbeiten. (diese werden Systemaufrufe aufrufen, aber diese sind sowieso unvermeidlich)
  • mit lesen/schreiben statt gepufferter I/O wird wahrscheinlich nicht die Mühe wert sein, die zusätzliche Buchhaltung wird Ihre Schleife fetter (BTW: mit fwrite () um ein Zeichen zu schreiben ist sicherlich verschwenderisch, gleiches für write())
  • vielleicht könnte ein größerer Ausgabepuffer helfen, aber ich würde nicht darauf zählen.
Verwandte Themen