GNU/Linux >> Linux Esercitazione >  >> Linux

Perché la stampa su stdout è così lenta? Si può velocizzare?

Grazie per tutti i commenti! Ho finito per rispondere da solo con il tuo aiuto. Sembra sporco rispondere alla tua stessa domanda, però.

Domanda 1:perché la stampa su stdout è lenta?

Risposta: Stampare su stdout non intrinsecamente lento. È il terminale con cui lavori che è lento. E non ha praticamente nulla a che fare con il buffering I/O sul lato dell'applicazione (ad esempio:buffering di file python). Vedi sotto.

Domanda 2:è possibile accelerare?

Risposta: Sì, può, ma apparentemente non dal lato del programma (il lato che esegue la "stampa" su stdout). Per velocizzarlo, usa un emulatore di terminale diverso più veloce.

Spiegazione...

Ho provato un programma terminale "leggero" autodefinito chiamato wterm e ha ottenuto in modo significativo risultati migliori. Di seguito è riportato l'output del mio script di test (in fondo alla domanda) durante l'esecuzione in wterm a 1920x1200 nello stesso sistema in cui l'opzione di stampa di base impiegava 12 secondi usando gnome-terminal:

-----
timing summary (100k lines each)
-----
print                         : 0.261 s
write to file (+fsync)        : 0.110 s
print with stdout = /dev/null : 0.050 s

0,26 è MOLTO meglio di 12! Non so se wterm è più intelligente su come viene visualizzato sullo schermo sulla falsariga di come stavo suggerendo (rendering della coda "visibile" a un frame rate ragionevole), o se semplicemente "fa meno" di gnome-terminal . Tuttavia, ai fini della mia domanda, ho la risposta. gnome-terminal è lento.

Quindi, se hai uno script a esecuzione prolungata che ritieni lento e invia enormi quantità di testo allo stdout... prova un terminale diverso e vedi se va meglio!

Nota che ho estratto praticamente a caso wterm dai repository ubuntu/debian. Questo collegamento potrebbe essere lo stesso terminale, ma non ne sono sicuro. Non ho testato nessun altro emulatore di terminale.

Aggiornamento:poiché dovevo grattare il prurito, ho testato un intero mucchio di altri emulatori di terminale con lo stesso script e schermo intero (1920x1200). Le mie statistiche raccolte manualmente sono qui:

wterm           0.3s
aterm           0.3s
rxvt            0.3s
mrxvt           0.4s
konsole         0.6s
yakuake         0.7s
lxterminal        7s
xterm             9s
gnome-terminal   12s
xfce4-terminal   12s
vala-terminal    18s
xvt              48s

I tempi registrati vengono raccolti manualmente, ma erano piuttosto coerenti. Ho registrato il valore migliore (ish). YMMV, ovviamente.

Come bonus, è stato un interessante tour di alcuni dei vari emulatori di terminale disponibili là fuori! Sono stupito che il mio primo test "alternativo" si sia rivelato il migliore del gruppo.


Il tuo reindirizzamento probabilmente non fa nulla poiché i programmi possono determinare se il loro output FD punta a un tty.

È probabile che stdout abbia un buffer di riga quando punta a un terminale (lo stesso del stdout di C comportamento del flusso).

Come esperimento divertente, prova a reindirizzare l'output a cat .

Ho provato il mio divertente esperimento, ed ecco i risultati.

$ python test.py 2>foo
...
$ cat foo
-----
timing summary (100k lines each)
-----
print                         : 6.040 s
write to file                 : 0.122 s
print with stdout = /dev/null : 0.121 s

$ python test.py 2>foo |cat
...
$ cat foo
-----
timing summary (100k lines each)
-----
print                         : 1.024 s
write to file                 : 0.131 s
print with stdout = /dev/null : 0.122 s

Com'è possibile che la scrittura su disco fisico sia MOLTO più veloce della scrittura sullo "schermo" (presumibilmente un'operazione tutta RAM), ed è effettivamente veloce quanto il semplice dumping nella spazzatura con /dev/null?

Congratulazioni, hai appena scoperto l'importanza del buffering I/O. :-)

Il disco appare per essere più veloce, perché è altamente bufferizzato:tutto write() di Python le chiamate vengono restituite prima che qualcosa venga effettivamente scritto sul disco fisico. (Il sistema operativo lo fa in un secondo momento, combinando molte migliaia di singole scritture in blocchi grandi ed efficienti.)

Il terminale, d'altra parte, fa poco o nessun buffering:ogni singolo print / write(line) attende il pieno scrivere (ovvero visualizzare sul dispositivo di output) per completare.

Per rendere equo il confronto, devi fare in modo che il file test utilizzi lo stesso buffer di output del terminale, cosa che puoi fare modificando il tuo esempio in:

fp = file("out.txt", "w", 1)   # line-buffered, like stdout
[...]
for x in range(lineCount):
    fp.write(line)
    os.fsync(fp.fileno())      # wait for the write to actually complete

Ho eseguito il test di scrittura del file sulla mia macchina e, con il buffering, anche qui è di 0,05 per 100.000 righe.

Tuttavia, con le modifiche di cui sopra per scrivere senza buffer, sono necessari 40 secondi per scrivere solo 1.000 righe su disco. Ho rinunciato ad aspettare 100.000 righe da scrivere, ma estrapolando dal precedente, ci vorrebbe più di un'ora .

Questo mette in prospettiva gli 11 secondi del terminale, vero?

Quindi, per rispondere alla tua domanda originale, scrivere su un terminale è in realtà incredibilmente veloce, tutto sommato, e non c'è molto spazio per renderlo molto più veloce (ma i singoli terminali variano in quanto lavoro fanno; vedi il commento di Russ a questo risposta).

(Potresti aggiungere più buffer di scrittura, come con l'I/O su disco, ma poi non vedresti cosa è stato scritto sul tuo terminale fino a quando il buffer non viene svuotato. È un compromesso:interattività contro efficienza di massa.)


Non posso parlare dei dettagli tecnici perché non li conosco, ma questo non mi sorprende:il terminale non è stato progettato per stampare tanti dati come questo. In effetti, fornisci persino un collegamento a un carico di roba GUI che deve fare ogni volta che vuoi stampare qualcosa! Nota che se chiami lo script con pythonw invece, non ci vogliono 15 secondi; questo è interamente un problema della GUI. Reindirizza stdout in un file per evitare questo:

import contextlib, io
@contextlib.contextmanager
def redirect_stdout(stream):
    import sys
    sys.stdout = stream
    yield
    sys.stdout = sys.__stdout__

output = io.StringIO
with redirect_stdout(output):
    ...

Linux
  1. Perché Scp è così lento e come renderlo più veloce?

  2. Perché non posso dividere un file .ape?

  3. Solo Root può montare, perché?

  4. Perché i file non possono essere manipolati da inode?

  5. Perché il mio accesso SSH è lento?

Perché uso rxvt come terminale

Perché non posso scegliere una cartella condivisa di VirtualBox?

Perché non posso terminare questo processo su Linux?

Perché non posso `tail -f /proc/$pid/fd/1`?

Perché non posso eliminare questo file come root?

Perché il mio rsync è così lento?