1. ホーム
  2. python

[解決済み] 標準出力への印刷が遅いのはなぜですか?速くすることはできますか?

2022-04-14 13:11:37

質問

printステートメントでターミナルに出力するだけでも時間がかかるので、いつも驚いたり、イライラしたりしています。 最近、ロギングがひどく遅くなったので、それを調べることにしました。 すべて は、ターミナルが結果を処理するのを待つために費やされる時間です。

stdoutへの書き込みを何とか早くできないか?

スクリプトを書きました(' print_timer.py にリダイレクトした場合、ファイルに書き込んだ場合、標準出力に100k行書き込んだ場合のタイミングを比較しました。 /dev/null . 以下は、そのタイミング結果です。

$ python print_timer.py
this is a test
this is a test
<snipped 99997 lines>
this is a test
-----
timing summary (100k lines each)
-----
print                         :11.950 s
write to file (+ fsync)       : 0.122 s
print with stdout = /dev/null : 0.050 s

すげーな。 pythonが裏でstdoutを/dev/nullに再割り当てしたことを認識するとか、そういうことをやっていないことを確認するために、スクリプトの外でリダイレクトを行いました...。

$ python print_timer.py > /dev/null
-----
timing summary (100k lines each)
-----
print                         : 0.053 s
write to file (+fsync)        : 0.108 s
print with stdout = /dev/null : 0.045 s

Pythonのトリックではなく、ターミナルだけなんですね。 dev/nullに出力をダンプすると速くなることは知っていましたが、それがそんなに重要だとは思いませんでした。

ttyの遅さには驚かされます。 物理ディスクへの書き込みは、スクリーンへの書き込み(おそらくオールRAMオペ)よりもずっと速く、事実上、/dev/nullでゴミ箱にダンプするのと同じくらい速いとはどういうことでしょうか?

このリンク 端末がI/Oをブロックすることで、どのようなことが可能になるかを説明しています。 入力の解析、フレームバッファの更新、ウィンドウのスクロールのためのXサーバとの通信、などなど。 ... でも、よくわからないんです。 何がそんなに時間がかかるのでしょうか?

私は、解決策はないと思っていますが(より高速なttyの実装がない限り)、とにかく聞いてみようと思いました。


UPDATE: いくつかのコメントを読んだ後、画面サイズが印刷時間に実際にどの程度影響するのか疑問に思っていました。 上の本当に遅い数字は、私のGnomeターミナルを1920x1200に拡大したときのものです。 もし私がそれを非常に小さくしたら...。

-----
timing summary (100k lines each)
-----
print                         : 2.920 s
write to file (+fsync)        : 0.121 s
print with stdout = /dev/null : 0.048 s

確かにその方が良いのですが(〜4倍)、私の疑問は変わりません。 それはただ 追加 というのも、なぜ端末の画面レンダリングが、標準出力に書き込むアプリケーションの速度を落とすのかがわからないからです。 なぜ、私のプログラムは画面描画を待つ必要があるのでしょうか?

すべてのターミナル/ttyアプリが同じように作られているわけではないのでしょうか? 私はまだ実験していません。 ターミナルは、すべての受信データをバッファリングし、目に見えないように解析/レンダリングし、現在の画面構成で表示されている最新のチャンクのみを適切なフレームレートでレンダリングできる必要があるように思います。 つまり、私がディスクへの書き込みと同期を 0.1 秒で行えるなら、ターミナルは同じ操作を同じ順番で完了できるはずです (その間に数回の画面更新があるかもしれませんが)。

この動作をプログラマーにとってより良いものにするために、アプリケーション側から変更できるttyの設定があることを、私はまだちょっと期待しています。 もし、これが厳密にターミナルアプリケーションの問題であるならば、これはStackOverflowに属するものではないのかもしれませんね。

何が足りないのでしょうか?


タイミングを生成するために使用したpythonプログラムはこちらです。

import time, sys, tty
import os

lineCount = 100000
line = "this is a test"
summary = ""

cmd = "print"
startTime_s = time.time()
for x in range(lineCount):
    print line
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)

#Add a newline to match line outputs above...
line += "\n"

cmd = "write to file (+fsync)"
fp = file("out.txt", "w")
startTime_s = time.time()
for x in range(lineCount):
    fp.write(line)
os.fsync(fp.fileno())
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)

cmd = "print with stdout = /dev/null"
sys.stdout = file(os.devnull, "w")
startTime_s = time.time()
for x in range(lineCount):
    fp.write(line)
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)

print >> sys.stderr, "-----"
print >> sys.stderr, "timing summary (100k lines each)"
print >> sys.stderr, "-----"
print >> sys.stderr, summary

解決方法は?

たくさんのコメントありがとうございました。 結局、皆さんに助けていただきながら自分で回答しています。 自分の質問に自分で答えるのは汚い気がしますが。

質問1:stdoutへの印刷が遅いのはなぜですか?

回答 stdoutへのプリントは ではない 本質的に遅いのです。 遅いのは、あなたが作業する端末の方です。 そして、アプリケーション側のI/Oバッファリング(例:Pythonのファイルバッファリング)とはほとんど関係がありません。 以下を参照してください。

疑問2:高速化できるのか?

回答 しかし、プログラム側(標準出力に「印刷」する側)から見ると、そうではないようです。 高速化するには、より高速な別の端末エミュレータを使用してください。

説明...

という自称「軽量」な端末プログラムを試してみました。 wterm となり かなり より良い結果を得ることができます。 以下は、私のテストスクリプト(質問の最下部)で実行したときの出力です。 wterm で、1920x1200 で、gnome-terminal を使って基本的な印刷オプションで12秒かかったのと同じシステムで、です。

-----
タイミングサマリ(各100k行)
-----
印刷:0.261秒
ファイルへの書き込み(+fsync) : 0.110 s
標準出力 = /dev/null での印刷 : 0.050 秒

0.26sは12sよりMUCH良い! 私は wterm は、私が提案した方法(妥当なフレームレートで「見える」尾部をレンダリングする)に沿って、画面へのレンダリング方法についてより知的であるか、あるいは、単に gnome-terminal . 私の質問の目的のために、私は答えを持っている、しかし。 gnome-terminal は遅い。

ですから、もしあなたが長時間実行するスクリプトが遅いと感じていて、標準出力に大量のテキストを吐き出しているなら、別のターミナルを試してみて、少しでも良くなるかどうか見てみてください

なお、私はかなりランダムに wterm をubuntu/debianのレポジトリから取得しました。 このリンク は、同じターミナルかもしれませんが、よくわかりません。 他のターミナルエミュレータはテストしていません。


更新:かゆいところに手が届くということで、同じスクリプトでフルスクリーン(1920x1200)にして他の端末エミュレータを一通りテストしてみました。 手動で収集した統計はここにあります。

wterm 0.3s
aterm 0.3秒
rxvt 0.3秒
mrxvt 0.4秒
コンソール 0.6秒
yakuake 0.7秒
lxterminal 7秒
xterm 9s
gnome-ターミナル 12秒
xfce4-ターミナル 12s
ヴァラ端子 18秒
xvt 48s

記録されたタイムは手動で収集したものですが、かなり安定していました。 ベストな値を記録しました。 もちろん、YMMVです。

また、様々な端末エミュレータを体験することができ、とても興味深いものでした。 私の最初の「代替」テストが、この中で一番良いものになったことに驚いています。