コメントありがとうございます!私はあなたの助けを借りて自分で答えることになりました。ただし、自分の質問に答えるのは汚い気がします.
質問 1:stdout への出力が遅いのはなぜですか?
答え: stdout への出力はできません 本質的に遅い。遅いのはあなたが使っている端末です。また、アプリケーション側の I/O バッファリング (例:python ファイルのバッファリング) とはほとんど関係ありません。以下を参照してください。
質問 2:高速化できますか?
答え: はい、できますが、プログラム側(標準出力への「印刷」を行う側)からではないようです。高速化するには、より高速な別の端末エミュレータを使用してください。
説明...
wterm
という自称「軽量」端末プログラムを試してみました かなり得た より良い結果。以下は、wterm
で実行したときのテスト スクリプトの出力です (質問の下部にあります)。 同じシステムで 1920x1200 で、基本的な印刷オプションが gnome-terminal を使用して 12 秒かかりました:
----- 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 秒は 12 秒よりもはるかに優れています! wterm
かどうかはわかりません 私が提案した方法(「目に見える」テールを適切なフレームレートでレンダリングする)に沿って画面にレンダリングする方法、または gnome-terminal
よりも「少ない」だけかどうかについて、よりインテリジェントです。 .しかし、私の質問の目的のために、私は答えを得ました。 gnome-terminal
遅いです。
そのため、長時間実行するスクリプトが遅いと感じ、標準出力に大量のテキストを出力する場合は、別の端末を試して、それが改善されるかどうかを確認してください!
wterm
をかなりランダムに引っ張ったことに注意してください ubuntu/debian リポジトリから。このリンクは同じ端末かもしれませんが、わかりません。他のターミナル エミュレータはテストしていません。
更新:かゆみを掻き立てる必要があったため、同じスクリプトと全画面表示 (1920x1200) を使用して、他のターミナル エミュレーターの山全体をテストしました。手動で収集した統計は次のとおりです:
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
記録された時間は手動で収集されますが、かなり一貫していました。最高の(らしい)値を記録しました。 YMMV、もちろんです。
おまけとして、そこにあるさまざまな端末エミュレーターの興味深いツアーでした。最初の「代替」テストが最高のものであることが判明したことに驚いています。
プログラムは出力 FD が tty を指しているかどうかを判断できるため、リダイレクトはおそらく何もしません。
端末を指すとき、stdout は行バッファリングされる可能性があります (C の stdout
と同じ) ストリーム動作)。
おもしろい実験として、出力を cat
にパイプしてみてください .
面白い実験をしてみました。結果はこちらです。
$ 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
<ブロック引用>
物理ディスクへの書き込みは、「画面」への書き込み (おそらくすべて RAM の操作) よりもはるかに高速であり、/dev/null を使用して単純にガベージにダンプするのと同じくらい高速であるというのはどうしてでしょうか?
おめでとうございます。I/O バッファリングの重要性を発見しました。 :-)
ディスクが表示されます 高度にバッファリングされているため、より高速になります:すべての Python の write()
実際に物理ディスクに何かが書き込まれる前に呼び出しが返されます。 (OS は後でこれを行い、何千もの個々の書き込みを大きな効率的なチャンクに結合します。)
一方、端末はバッファリングをほとんど、またはまったく行いません。個々の print
/ write(line)
いっぱいになるのを待ちます 書き込み (つまり、出力デバイスへの表示) して完了します。
公平に比較するには、ファイル テストで端末と同じ出力バッファリングを使用する必要があります。これは、例を次のように変更することで実行できます。
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
私のマシンでファイル書き込みテストを実行しましたが、バッファリングを使用して、100,000 行で 0.05 秒もかかりました.
ただし、バッファなしで書き込むための上記の変更では、1,000 行だけをディスクに書き込むのに 40 秒かかります。 100,000 行を書き込むのを待つことをあきらめましたが、前の例から推測すると、1 時間以上かかるでしょう .
それは端末の 11 秒を視野に入れていますよね?
したがって、元の質問に答えるために、端末への書き込みは実際には非常に高速であり、すべてを考慮すると、はるかに高速にする余地はあまりありません(ただし、個々の端末は、実行する作業の量が異なります。これに対するラスのコメントを参照してください答えてください)。
(ディスク I/O のように、書き込みバッファリングを追加することもできますが、バッファがフラッシュされるまで、端末に何が書き込まれたかを確認できません。これはトレードオフです:対話性とバルク効率です。)
技術的な詳細についてはよく知らないので語ることはできませんが、これは驚きではありません。端末は、このような大量のデータを出力するようには設計されていません。実際、何かを印刷するたびに実行しなければならない GUI の負荷へのリンクを提供することさえできます。 pythonw
でスクリプトを呼び出すと、 代わりに、15 秒かかりません。これは完全に GUI の問題です。 stdout
をリダイレクト これを避けるためにファイルに:
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):
...