2016-01-06 15 views
5

Ho un programma Go che scrive stringhe in un file. Ho un ciclo che è iterato 20000 volte e in ogni iterazione sto scrivendo circa 20-30 stringhe in un file. Volevo solo sapere qual è il modo migliore per scriverlo in un file.Write Operation Cost [Go lang]

  • Approccio 1: Tenere aperto il puntatore del file all'inizio del codice e scrivere per ogni corda. Rende le operazioni di scrittura 20000 * 30.

  • Approccio 2: Usa bytes.Buffer Go e memorizzare tutto nel buffer e scriverlo al end.Also in questo caso dovrebbe essere il puntatore del file aperto dall'inizio del codice o alla fine della codice. è importante?

Sto assumendo che l'approccio 2 dovrebbe funzionare meglio. Qualcuno può confermarlo con una ragione. In che modo scrivere allo stesso tempo è meglio che scrivere periodicamente. Perché il puntatore del file sarà comunque aperto. Sto usando f.WriteString(<string>) e buffer.WriteString(<some string>) buffer è di tipo bytes.Buffer e f è il puntatore del file aperto.

risposta

3

Le operazioni che richiedono tempo durante la scrittura nei file sono le syscalls e l'I/O del disco. Il fatto che il puntatore del file sia aperto non ti costa nulla. In modo così ingenuo, potremmo dire che il secondo metodo è il migliore.

Ora, come forse saprai, il tuo sistema operativo non scrive direttamente nei file, utilizza una cache interna in memoria per i file che vengono scritti e eseguono il vero I/O in seguito. Non conosco i particolari di questo, e in generale non ne ho bisogno.

Quello che vorrei consigliare è una soluzione di medio livello: fare un buffer per ogni iterazione del ciclo e scrivere questo N volte. In questo modo si taglia una grande parte del numero di sysc e di (potenzialmente) scritture su disco, ma senza consumare troppa memoria con il buffer (che dipende dalla dimensione delle stringhe, che il mio sia un punto da prendere in considerazione).

Vorrei suggerire il benchmarking per la soluzione migliore, ma a causa della memorizzazione nella cache eseguita dal sistema, l'I/O del disco di benchmarking è un vero incubo.

+0

questo è ciò che ['bufio'] (https://golang.org/pkg/bufio/) è per. – JimB

1

Le scale di sicurezza non sono economiche, quindi il secondo approccio è migliore.

È possibile utilizzare lat_syscall strumento da lmbench per misurare quanto tempo ci vuole per chiamare singolo write:

$ ./lat_syscall write 
Simple write: 0.1522 microseconds 

Quindi, sul mio sistema ci vorranno circa 20000 * 0.15μs = 3 ms di tempo in più solo per telefonare write per ogni stringa.

5

Il pacchetto bufio è stato creato esattamente per questo tipo di attività. Invece di creare un syscall per ogni chiamata Write bufio.Writer buffer fino a un numero fisso di byte nella memoria interna prima di eseguire un syscall.Dopo una chiamata di sistema buffer interno viene riutilizzato per la successiva porzione di dati

Confrontando al secondo approccio bufio.Writer

  • rende più syscalls (N/S anziché 1)
  • utilizza meno memoria (S byte anziché N bytes)

dove S - si dimensione del buffer (può essere impostato tramite bufio.NewWriterSize), N - dimensione totale dei dati che devono essere scritti.

Esempio di utilizzo (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

In che modo il secondo approccio richiede una quantità di memoria fissa. Puoi spiegarlo per favore? perché il buffer continua a crescere in base ai requisiti. – KD157

+0

sì vero grazie per la modifica, ma scrivere ancora tutti i byte contemporaneamente renderebbe più veloce? Diciamo che ci sono 20.000 stringhe con 1000 caratteri ciascuna. È appena 20 MB. – KD157

+0

Puoi misurarlo. Ma sovraccarico di 5000 syscalls che sono necessari per scrivere 20Mb con buffer da 4kb è inferiore a un millisecondo. Inoltre, a meno che non si conosca la dimensione finale dei dati che è necessario scrivere prima di creare byte, Buffer, sarà probabilmente ridimensionato (bytes.Buffer) e rallenterà sicuramente l'app. – kostya

0

avrei dubbio l'overhead e la complessità del codice di usare un bufio.Writer invece di io.Writer quando il sistema operativo sarebbe già buffer prima una sincronizzazione su disco. È come il buffering di un buffer. Penso che tu debba davvero fare un benchmark sul tuo sistema per vedere che vale la pena di presentarlo per sempre nel tuo codice.