2009-09-01 16 views
20

Dopo aver fornito lo stesso programma che legge un file di input generato casualmente ed echeggia la stessa stringa che legge su un'uscita. L'unica differenza è che da un lato sto fornendo i metodi di lettura e scrittura da Linux syscalls, e dall'altro lato sto usando fread/fwrite.Perché la funzione fwrite libc è più veloce della funzione di scrittura syscall?

Temporizzando la mia applicazione con un input di 10 Mb di dimensione e facendo eco a/dev/null e assicurandomi che il file non sia memorizzato nella cache, ho trovato che fwrite di libc è più veloce di una scala LARGE quando si usano buffer molto piccoli (1 byte nel caso).

Qui è la mia uscita da tempo, utilizzando fwrite:

real 0m0.948s 
user 0m0.780s 
sys  0m0.012s 

E utilizzando la syscall scrittura:

real 0m8.607s 
user 0m0.972s 
sys  0m7.624s 

L'unica possibilità che mi viene in mente è che internamente libc è già il buffering mia input ... Purtroppo non ho trovato molte informazioni sul web, quindi forse i guru qui potrebbero aiutarmi.

+4

"internamente libc sta già caricando il buffer del mio input". Questo è esattamente ciò che sta facendo. Probabilmente puoi anche leggere il codice sorgente di libc se vuoi, e vedere esattamente come lo sta facendo. – kquinn

risposta

29

Timing mia applicazione con un ingresso di 10Mb in termini di dimensioni e facendo eco alla /dev/null, e assicurandosi che il file in non memorizzata nella cache, ho trovato che frwite di libc è più veloce da un ampio scala quando utilizza buffer molto piccoli (1 byte nel caso ).

fwrite funziona su flussi, che vengono memorizzati nel buffer. Pertanto molti buffer di piccole dimensioni saranno più veloci perché non eseguiranno una costosa chiamata di sistema finché il buffer non si riempie (o lo si scarica o si chiude lo stream). D'altra parte, piccoli buffer inviati a write verrà eseguito una chiamata di sistema costoso per ogni buffer - è lì che si sta perdendo la velocità. Con un buffer di flusso di 1024 byte e buffer di scrittura 1 byte, si sta guardando 1024 write chiamate per ogni kilobyte , piuttosto che 1024 fwrite chiamate trasformando in una write - vedere la differenza?

Per i buffer di grandi dimensioni, la differenza è minima, poiché il numero di buffer è inferiore e pertanto il numero di chiamate di sistema è più costante tra fwrite e write.

In altre parole, fwrite(3) è solo una routine di libreria che raccoglie l'output in blocchi e quindi chiama write(2). Ora, write(2), è una chiamata di sistema che trappole nel kernel. Ecco dove avviene effettivamente l'I/O. C'è un po 'di overhead per chiamare semplicemente nel kernel, e poi c'è il tempo necessario per scrivere qualcosa. Se si utilizza buffer di grandi dimensioni, vi accorgerete che write(2) è più veloce perché ha finalmente essere chiamato in ogni caso, e se si sta scrivendo una o più volte al fwrite quindi il sovraccarico di buffer fwrite è proprio questo: più in alto.

Se si desidera saperne di più, è possibile dare un'occhiata a this document, che spiega i flussi I/O standard.

14

write (2) è l'operazione fondamentale kernel.

fwrite (3) è una funzione di libreria che aggiunge il buffering a write (2).

Per i conteggi di byte piccoli (ad esempio, linea per volta), fwrite (3) è più veloce, a causa del sovraccarico dovuto al solo fare una chiamata al kernel.

Per il conteggio dei byte di grandi dimensioni (blocco I/O), write (2) è più veloce, perché non si preoccupa del buffering e si deve chiamare il kernel in entrambi i casi.

Se si guarda la sorgente su cp (1), non verrà visualizzato alcun buffering.

Infine, c'è un'ultima considerazione: ISO C vs Posix. Le funzioni della libreria bufferizzata come fwrite sono specificate in ISO C mentre le chiamate del kernel come write sono Posix. Mentre molti sistemi rivendicano la compatibilità con Posix, specialmente quando si cerca di qualificarsi per contratti governativi, in pratica è specifico per i sistemi di tipo Unix. Quindi, le operazioni bufferizzate sono più portabili. Di conseguenza, un Linux cp utilizzerà sicuramente write ma un programma C che deve funzionare su più piattaforme potrebbe dover utilizzare fwrite.

+0

Recentemente ho avuto un'intervista e ho dato lo stesso ragionamento sulla differenza in bianco e nero di scrittura e scrittura e la risposta che ho ottenuto è stata "la conoscenza di questa differenza è completamente falsa" !!. L'intervistatore mi è sembrato molto arrogante. Comunque, volevo solo confermare, se c'è qualche altra differenza tra le chiamate fatte anche se glibc e le chiamate fatte direttamente al kernel? –

+0

@PK, ho aggiornato la mia risposta ... – DigitalRoss

Problemi correlati