qsubを使用してキューに送信されたPythonコードを実行するSGEスクリプトがあります。Pythonスクリプトには、いくつかの印刷ステートメントがあります(プログラムの進行状況を更新しています)。コマンド ラインから python スクリプトを実行すると、print ステートメントが stdout に送信されます。sge スクリプトでは、-o オプションを使用して出力をファイルにリダイレクトします。ただし、スクリプトは、python スクリプトの実行が完了した後にのみこれらをファイルに送信するようです。(a) プログラムでリアルタイムの更新を表示できなくなり、(b) ジョブが正しく終了しない場合 (たとえば、ジョブがキューから開始された場合)、更新がまったく印刷されないため、これは面倒です。最後にすべてをまとめるのではなく、何かを印刷するたびにスクリプトがファイルに書き込んでいることを確認するにはどうすればよいですか?
7 に答える
バッファリングされた出力で問題が発生していると思います。Pythonはライブラリを使用してその出力を処理します。ライブラリは、ttyと通信していないときにブロックを書き込む方が効率的であることを認識しています。
これを回避するには、いくつかの方法があります。たとえば、スクリプトの最初の行として次のようなものを使用して、「-u」オプションを使用してPythonを実行できます(詳細については、Pythonのマニュアルページを参照してください)。
#! /usr/bin/python -u
ただし、「/ usr / bin / env」トリックを使用している場合は、Pythonがインストールされている場所がわからないため、これは機能しません。
別の方法は、次のようなものでstdoutを再度開くことです。
import sys
import os
# reopen stdout file descriptor with write mode
# and 0 as the buffer size (unbuffered)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
os.fdopenのbufsizeパラメーターが0に設定されていることに注意してください。これにより、強制的にバッファーが解除されます。sys.stderrで同様のことを行うことができます。
他の人が述べたように、tty に接続されていないときに常に stdout を書き込むとは限らないのは、パフォーマンス上の理由からです。
stdout を書きたい特定のポイントがある場合は、それを強制することができます。
import sys
sys.stdout.flush()
その時点で。
SGE で同様の問題が発生しましたが、ファイル IO を「バッファリング解除」するための推奨される方法はありませんでした。出力を確認するには、プログラムの実行が終了するまで待たなければなりませんでした。
私が見つけた回避策は、「書き込み」メソッドを再実装するカスタム オブジェクトに sys.stdout をラップすることでした。この新しいメソッドは、実際に stdout に書き込む代わりに、IO がリダイレクトされるファイルを開き、目的のデータを追加してから、ファイルを閉じます。少し醜いですが、実際にファイルを開いたり閉じたりするとIOがインタラクティブになるため、問題が解決したことがわかりました。
最小限の例を次に示します。
import os, sys, time
class RedirIOStream:
def __init__(self, stream, REDIRPATH):
self.stream = stream
self.path = REDIRPATH
def write(self, data):
# instead of actually writing, just append to file directly!
myfile = open( self.path, 'a' )
myfile.write(data)
myfile.close()
def __getattr__(self, attr):
return getattr(self.stream, attr)
if not sys.stdout.isatty():
# Detect redirected stdout and std error file locations!
# Warning: this will only work on LINUX machines
STDOUTPATH = os.readlink('/proc/%d/fd/1' % os.getpid())
STDERRPATH = os.readlink('/proc/%d/fd/2' % os.getpid())
sys.stdout=RedirIOStream(sys.stdout, STDOUTPATH)
sys.stderr=RedirIOStream(sys.stderr, STDERRPATH)
# Simple program to print msg every 3 seconds
def main():
tstart = time.time()
for x in xrange( 10 ):
time.sleep( 3 )
MSG = ' %d/%d after %.0f sec' % (x, args.nMsg, time.time()-tstart )
print MSG
if __name__ == '__main__':
main()
これは、プロセスの出力を SGE がバッファリングすることです。これは、python プロセスであろうと他のプロセスであろうと発生します。
一般に、SGE のバッファリングを変更して再コンパイルすることで、バッファリングを減少または無効にすることができます。しかし、これは良いことではありません。すべてのデータがゆっくりとディスクに書き込まれるため、全体的なパフォーマンスに影響します。
今日も同じ問題に遭遇し、印刷する代わりにディスクに書き込むだけで解決しました。
with open('log-file.txt','w') as out:
out.write(status_report)