Pythonのファイル書き込み高速化

事の始まり

私は今、Pythonで、約1万項目の変換テーブルを使って、数百のファイルにある数万行のデータ変換を行っている。(ちなみに、書き込み前に、置換という名の削除を行っている)

変換自体は行単位であり、猶且変換テーブルにデータがあることを保証しているので、辞書型を使って行っている。

このプログラムを走らせた時、非常に残念なことに、極めて低いパフォーマンスしか出なかった。

Profile

従って、パフォーマンスチューニングのセオリー通り、まずはクリティカルパスを探す為に、Profileを行う。

その結果はこんな感じ

予想に反して、writeメソッドが凄い勢いで時間を食っているのである。書き込み先がSSDなのに、である。

改善してみる

Profile結果からソースコードを眺めてみると、今回クリティカルパスは以下の部分だと目星がついた。

for line in var:                     
    line = multipleReplace(line, adict, rx)
    f.write( line + "\n")      

multipleReplaceはwriteよりも少ないが、そこそこ重い処理である。今回は取り上げない。

これを以下のように書き換えてみた。

lines = []
for line in var:
    lines.append( multipleReplace(line, adict, rx) )
with codecs.open(fName, 'w+', CODEC) as f:
    f.write( "\n".join(lines) )

その結果は

と劇的である。

今回のキモ

writeの複数回呼び出しは、パフォーマンスを著しく低下させる。従って、メモリを圧迫しない程度にまでメモリ上に纏めてから保存すべきである。

また、今回特に触れなかったが、Python Cookbookによると、文字列の結合は、最後に纏めてjoinを用いるのが最も早い、ということである。文字列は変更不能なので、逐次結合をしていくと、無駄にオブジェクトを生成するためのようだ。

余談

writeはこちらの方が早いが、Readに関してはそうでもなく、寧ろ纏めて読み込んでから分割するよりも、for文で回した方が早いようだ。