Terima kasih atas semua komentarnya! Saya akhirnya menjawabnya sendiri dengan bantuan Anda. Rasanya kotor menjawab pertanyaan Anda sendiri.
Pertanyaan 1:Mengapa pencetakan ke stdout lambat?
Jawaban: Mencetak ke stdout tidak inheren lambat. Ini adalah terminal tempat Anda bekerja yang lambat. Dan hampir tidak ada hubungannya dengan buffering I/O di sisi aplikasi (misalnya:buffering file python). Lihat di bawah.
Pertanyaan 2:Bisakah dipercepat?
Jawaban: Ya bisa, tapi sepertinya bukan dari sisi program (sisi yang melakukan 'printing' ke stdout). Untuk mempercepatnya, gunakan emulator terminal lain yang lebih cepat.
Penjelasan...
Saya mencoba program terminal 'ringan' yang dijelaskan sendiri bernama wterm
dan mendapatkan secara signifikan hasil yang lebih baik. Di bawah ini adalah output dari skrip pengujian saya (di bagian bawah pertanyaan) saat dijalankan di wterm
pada 1920x1200 pada sistem yang sama di mana opsi cetak dasar membutuhkan waktu 12 detik menggunakan 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 detik JAUH lebih baik daripada 12 detik! Saya tidak tahu apakah wterm
lebih cerdas tentang cara merender ke layar sesuai dengan yang saya sarankan (render ekor 'terlihat' pada frekuensi gambar yang masuk akal), atau apakah itu hanya "melakukan kurang" dari gnome-terminal
. Untuk keperluan pertanyaan saya, saya punya jawabannya. gnome-terminal
lambat.
Jadi - Jika Anda memiliki skrip yang berjalan lama yang Anda rasa lambat dan memuntahkan teks dalam jumlah besar ke stdout... coba terminal lain dan lihat apakah ada yang lebih baik!
Perhatikan bahwa saya menarik wterm
secara acak dari repositori ubuntu/debian. Tautan ini mungkin terminal yang sama, tapi saya tidak yakin. Saya tidak menguji emulator terminal lainnya.
Pembaruan:Karena saya harus menggaruk gatal, saya menguji tumpukan emulator terminal lainnya dengan skrip yang sama dan layar penuh (1920x1200). Statistik saya yang dikumpulkan secara manual ada di sini:
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
Waktu yang direkam dikumpulkan secara manual, tetapi cukup konsisten. Saya mencatat nilai (ish) terbaik. YMMV, tentu saja.
Sebagai bonus, ini adalah tur yang menarik dari berbagai emulator terminal yang tersedia di luar sana! Saya kagum tes 'alternatif' pertama saya ternyata yang terbaik.
Pengalihan Anda mungkin tidak melakukan apa-apa karena program dapat menentukan apakah FD keluarannya mengarah ke tty.
Kemungkinan stdout disangga baris saat menunjuk ke terminal (sama dengan stdout
C perilaku streaming).
Sebagai eksperimen yang menyenangkan, coba salurkan hasilnya ke cat
.
Saya telah mencoba eksperimen lucu saya sendiri, dan inilah hasilnya.
$ 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
Bagaimana mungkin menulis ke disk fisik JAUH lebih cepat daripada menulis ke "layar" (mungkin operasi semua-RAM), dan efektif secepat hanya membuang ke sampah dengan /dev/null?
Selamat, Anda baru saja mengetahui pentingnya I/O buffering. :-)
Disk muncul menjadi lebih cepat, karena sangat buffered:semua write()
Python panggilan dikembalikan sebelum sesuatu benar-benar ditulis ke disk fisik. (OS melakukan ini nanti, menggabungkan ribuan penulisan individu menjadi potongan besar dan efisien.)
Terminal, di sisi lain, melakukan sedikit atau tidak ada buffering:masing-masing print
/ write(line)
menunggu penuh menulis (yaitu menampilkan ke perangkat output) untuk menyelesaikan.
Agar perbandingannya adil, Anda harus membuat pengujian file menggunakan buffer keluaran yang sama dengan terminal, yang dapat Anda lakukan dengan memodifikasi contoh Anda menjadi:
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
Saya menjalankan tes penulisan file Anda di mesin saya, dan dengan buffering, ini juga 0,05 detik di sini untuk 100.000 baris.
Namun, dengan modifikasi di atas untuk menulis tanpa buffer, dibutuhkan 40 detik untuk menulis hanya 1.000 baris ke disk. Saya menyerah menunggu 100.000 baris untuk ditulis, tetapi mengekstrapolasi dari yang sebelumnya, akan memakan waktu lebih dari satu jam .
Itu menempatkan 11 detik terminal ke dalam perspektif, bukan?
Jadi untuk menjawab pertanyaan awal Anda, menulis ke terminal sebenarnya sangat cepat, semua hal dipertimbangkan, dan tidak ada banyak ruang untuk membuatnya lebih cepat (tetapi masing-masing terminal bervariasi dalam seberapa banyak pekerjaan yang mereka lakukan; lihat komentar Russ untuk ini jawab).
(Anda dapat menambahkan lebih banyak buffering tulis, seperti dengan disk I/O, tetapi kemudian Anda tidak akan melihat apa yang ditulis ke terminal Anda sampai setelah buffer dihapus. Ini adalah trade-off:interaktivitas versus efisiensi massal.)
Saya tidak dapat membicarakan detail teknisnya karena saya tidak mengetahuinya, tetapi ini tidak mengejutkan saya:terminal tidak dirancang untuk mencetak banyak data seperti ini. Memang, Anda bahkan memberikan tautan ke banyak hal GUI yang harus dilakukan setiap kali Anda ingin mencetak sesuatu! Perhatikan bahwa jika Anda memanggil skrip dengan pythonw
sebaliknya, tidak butuh 15 detik; ini sepenuhnya masalah GUI. Alihkan stdout
ke file untuk menghindari ini:
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):
...