2016-01-06 18 views
5

Ich habe ein Go-Programm, das Strings in eine Datei schreibt. Ich habe eine Schleife, die 20000 mal iteriert wird und in jeder Iteration schreibe ich etwa 20-30 Strings in eine Datei. Ich wollte nur wissen, welches der beste Weg ist, um es in eine Datei zu schreiben.Schreibvorgang Kosten [Go lang]

  • Ansatz 1: Halten Sie öffnen den Dateizeiger am Anfang des Codes und schreiben es für jeden String. Es macht 20000 * 30 Schreiboperationen.

  • Ansatz 2: Verwenden bytes.Buffer Go und speichert alles im Puffer und schreiben sie an der end.Also in diesem Fall sollte der Dateizeiger vom Anfang des Codes oder am Ende der geöffnet werden Code. Ist es wichtig?

Ich gehe davon aus Ansatz 2 sollte besser funktionieren. Kann jemand das mit einem Grund bestätigen? Wie ist das Schreiben auf einmal besser als das regelmäßige Schreiben? Weil der Dateizeiger sowieso geöffnet ist. Ich verwende f.WriteString(<string>) und buffer.WriteString(<some string>) Puffer ist vom Typ bytes.Buffer und f ist der Dateizeiger geöffnet.

Antwort

3

Die Vorgänge, die beim Schreiben in Dateien Zeit benötigen, sind die Systemaufrufe und die Platten-E/A. Die Tatsache, dass der Dateizeiger geöffnet ist, kostet Sie nichts. So naiv könnten wir sagen, dass die zweite Methode die beste ist.

Nun, wie Sie vielleicht wissen, Ihr Betriebssystem schreibt nicht direkt in Dateien, es verwendet einen internen In-Memory-Cache für Dateien, die geschrieben werden und die tatsächlichen I/O später. Ich kenne die genauen Details nicht, und im Allgemeinen muss ich nicht.

Was ich empfehlen würde ist eine mittlere Lösung: Machen Sie einen Puffer für jede Schleife Iteration, und schreiben Sie diese N mal. Auf diese Weise wird ein großer Teil der Anzahl von Systemaufrufen und (möglicherweise) Festplattenschreibvorgängen abgeschnitten, aber ohne zu viel Speicher mit dem Puffer zu verbrauchen (abhängig von der Größe Ihrer Strings, das ist ein Punkt, der berücksichtigt werden muss).

Ich würde vorschlagen, Benchmarking für die beste Lösung, aber aufgrund der Zwischenspeicherung durch das System gemacht, Benchmarking Disk I/O ist ein echter Albtraum.

+0

Dies ist, was ['bufio'] (https://golang.org/pkg/bufio/) ist. – JimB

1

Syscalls sind nicht billig, also ist der zweite Ansatz besser.

Sie können lat_syscall Tool von lmbench verwenden, um zu messen, wie lange es zu nennen nimmt einzelne write:

$ ./lat_syscall write 
Simple write: 0.1522 microseconds 

Also, auf meinem System dauert es etwa 20000 * 0.15μs = zusätzliche Zeit 3ms nur write anrufen für jeden String.

5

bufio Paket wurde genau für diese Art von Aufgabe erstellt. Anstatt einen Syscall für jeden Schreibaufruf durchzuführen, puffert bufio.Writer bis zu einer festen Anzahl von Bytes im internen Speicher, bevor ein Syscall ausgeführt wird.Nach einer Syscall wird der interne Puffer für den nächsten Teil von Daten

auf Ihren zweiten Ansatz bufio.Writer

  • mehr syscalls Vergleich wiederverwendet (N/S anstelle von 1)
  • verwendet weniger Speicher (S Bytes statt macht N bytes)

wo S - Puffergröße wird (überangegeben werden, 0), N - Gesamtgröße der Daten, die geschrieben werden müssen.

Beispiel zur Nutzung (https://play.golang.org/p/AvBE1d6wpT):

f, err := os.Create("file.txt") 
if err != nil { 
    log.Fatal(err) 
} 
defer f.Close() 

w := bufio.NewWriter(f) 
fmt.Fprint(w, "Hello, ") 
fmt.Fprint(w, "world!") 
err = w.Flush() // Don't forget to flush! 
if err != nil { 
    log.Fatal(err) 
} 
+0

Wie benötigt der zweite Ansatz eine festgelegte Speichermenge? Kannst du das bitte erklären? weil der Puffer entsprechend der Anforderung wächst. – KD157

+0

ja true danke für die Bearbeitung, aber immer noch alle Bytes auf einmal schreiben würde es schneller machen? Sagen wir, es gibt 20.000 Zeichenfolgen mit jeweils 1000 Zeichen. Es ist kaum 20 MB. – KD157

+0

Sie können es messen. Aber der Overhead von 5000 Systemaufrufen, die benötigt werden, um 20 MB mit 4 KB Puffer zu schreiben, ist weniger als eine Millisekunde. Auch wenn Sie die endgültige Größe der Daten kennen, die Sie schreiben müssen, bevor Sie Bytes erstellen, Puffer, wird es (bytes.Buffer) wahrscheinlich neu skaliert werden müssen und es wird Ihre App sicherlich verlangsamen. – kostya

0

Ich würde den Aufwand und die Komplexität des Codes in Frage stellen eine bufio.Writer statt nur io.Writer der Verwendung, wenn das OS bereits Puffer vor einem Sync auf die Festplatte wäre. Es ist wie Puffer puffern. Ich denke, Sie müssen wirklich auf Ihrem System Benchmark, um zu sehen, dass es sich lohnt, für immer in Ihrem Code einzuführen.