会社でRADIUSサーバーの認証ログをリアルタイムに解析するツールを作っていたところ、思ったようなパフォーマンスが出ず、line_profiler というプロファイリングツールを使って調べたところ、処理時間の大部分を datetime.strptime で日時の文字列を datetime オブジェクトに変換する一行のコードが占めていました。
そこで、日時の文字列を自分で区切って datetime.datetime() に渡すようにしたところ、処理速度が飛躍的に向上しボトルネックは無くなりました。
from datetime import datetime import time logtime_str = '20170101123456' start = time.time() for i in range(100000): dt = datetime.strptime(logtime_str, '%Y%m%d%H%M%S') print('strptime: {:.4f}sec, dt:{}'.format(time.time() - start, dt)) # strptime: 2.2368sec, dt:2017-01-01 12:34:56
from datetime import datetime import time logtime_str = '20170101123456' start = time.time() for i in range(100000): dt = datetime( year=int(logtime_str[0:4]), month=int(logtime_str[4:6]), day=int(logtime_str[6:8]), hour=int(logtime_str[8:10]), minute=int(logtime_str[10:12]), second=int(logtime_str[12:14]) ) print('self parse: {:.4f}sec, dt:{}'.format(time.time() - start, dt)) # self parse: 0.5266sec, dt:2017-01-01 12:34:56
上記サンプルコードを使ってベンチマークをしたところ、このように4倍以上高速になっていることがわかります。(実行環境: macOS Sierra / Python 3.5.2)
Python 標準のプロファイリングライブラリ cProfile を視覚化する Python Call Graph というツールを使ってみました。正規表現は使い回しされているようですが、なぜか毎回ロケール情報を取りに行っていて結構な時間がかかっているようです。今回のコードでは使っていませんが、strptime は曜日( 'Monday', '月曜' )なども取り扱えるため、ロケールを解釈する処理が入っているのでしょうか。でも、それを除いても時間かかりすぎてません・・・?使い方間違ってるのかな・・・。
テキストのパースは奥が深いことはわかりますが、よく使う標準ライブラリにこのような罠があるというのは少しショックです。。。